1use std::collections::HashMap;
9
10use crate::reader::Cursor;
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub enum CmpOp {
15 Eq,
16 Ne,
17 Lt,
18 Ge,
19 Gt,
20 Le,
21}
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq)]
25pub enum ArithOp {
26 Add,
27 Sub,
28 Mul,
29 Div,
30 Rem,
31 And,
32 Or,
33 Xor,
34 Shl,
35 Shr,
36 Ushr,
37}
38
39#[derive(Debug, Clone, Copy, PartialEq, Eq)]
41pub enum CmpKind {
42 CmplF,
43 CmpgF,
44 CmplD,
45 CmpgD,
46 CmpJ,
47}
48
49#[derive(Debug, Clone, Copy, PartialEq, Eq)]
50pub enum InvokeKind {
51 Virtual,
52 Super,
53 Direct,
54 Static,
55 Interface,
56 Polymorphic,
57}
58
59#[derive(Debug, Clone)]
61pub enum InsnKind {
62 Nop,
63 Move {
65 dst: u16,
66 src: u16,
67 },
68 MoveResult {
69 dst: u16,
70 },
71 MoveException {
72 dst: u16,
73 },
74 ReturnVoid,
75 Return {
76 src: u16,
77 },
78 Const {
80 dst: u16,
81 val: i64,
82 wide: bool,
83 },
84 ConstClass {
86 dst: u16,
87 type_idx: u32,
88 },
89 ConstString {
90 dst: u16,
91 str_idx: u32,
92 },
93 MonitorEnter {
94 reg: u16,
95 },
96 MonitorExit {
97 reg: u16,
98 },
99 CheckCast {
101 reg: u16,
102 type_idx: u32,
103 },
104 InstanceOf {
105 dst: u16,
106 src: u16,
107 type_idx: u32,
108 },
109 ArrayLength {
110 dst: u16,
111 src: u16,
112 },
113 NewInstance {
114 dst: u16,
115 type_idx: u32,
116 },
117 NewArray {
118 dst: u16,
119 size: u16,
120 type_idx: u32,
121 },
122 FilledNewArray {
123 regs: Vec<u16>,
124 type_idx: u32,
125 },
126 FillArrayData {
128 reg: u16,
129 payload_pc: u32,
130 },
131 Throw {
132 reg: u16,
133 },
134 Goto {
136 target: u32,
137 },
138 PackedSwitch {
139 reg: u16,
140 payload_pc: u32,
141 },
142 SparseSwitch {
143 reg: u16,
144 payload_pc: u32,
145 },
146 Cmp {
147 dst: u16,
148 a: u16,
149 b: u16,
150 kind: CmpKind,
151 },
152 If {
154 op: CmpOp,
155 a: u16,
156 b: u16,
157 z: bool,
158 target: u32,
159 },
160 AGet {
161 dst: u16,
162 array: u16,
163 index: u16,
164 ty: char,
165 },
166 APut {
167 value: u16,
168 array: u16,
169 index: u16,
170 ty: char,
171 },
172 IGet {
173 dst: u16,
174 obj: u16,
175 field_idx: u32,
176 },
177 IPut {
178 value: u16,
179 obj: u16,
180 field_idx: u32,
181 },
182 SGet {
183 dst: u16,
184 field_idx: u32,
185 },
186 SPut {
187 value: u16,
188 field_idx: u32,
189 },
190 Invoke {
191 kind: InvokeKind,
192 regs: Vec<u16>,
193 method_idx: u32,
194 },
195 Un {
197 dst: u16,
198 src: u16,
199 op: UnArith,
200 from: char,
201 to: char,
202 },
203 Bin {
205 op: ArithOp,
206 dst: u16,
207 a: u16,
208 b: u16,
209 ty: char,
210 },
211 BinLit {
213 op: ArithOp,
214 dst: u16,
215 a: u16,
216 lit: i32,
217 rsub: bool,
218 },
219 ConstMethodHandle {
221 dst: u16,
222 handle_idx: u32,
223 },
224 ConstMethodType {
226 dst: u16,
227 proto_idx: u32,
228 },
229 InvokeCustom {
232 call_site_idx: u32,
233 regs: Vec<u16>,
234 },
235 Unknown,
237}
238
239#[derive(Debug, Clone, Copy, PartialEq, Eq)]
240pub enum UnArith {
241 Neg,
242 Not,
243 Conv,
244}
245
246#[derive(Debug, Clone)]
247pub enum Payload {
248 Packed { first_key: i32, targets: Vec<i32> },
250 Sparse { pairs: Vec<(i32, i32)> },
252 ArrayData {
254 elem_width: u16,
255 size: u32,
256 data: Vec<u8>,
257 },
258}
259
260#[derive(Debug, Clone)]
262pub struct Insn {
263 pub pc: u32,
265 pub size: u32,
267 pub op: u8,
268 pub kind: InsnKind,
269}
270
271impl Insn {
272 pub fn is_branch(&self) -> bool {
273 matches!(
274 self.kind,
275 InsnKind::Goto { .. }
276 | InsnKind::If { .. }
277 | InsnKind::PackedSwitch { .. }
278 | InsnKind::SparseSwitch { .. }
279 )
280 }
281
282 pub fn is_terminator(&self) -> bool {
283 matches!(
284 self.kind,
285 InsnKind::ReturnVoid | InsnKind::Return { .. } | InsnKind::Throw { .. }
286 )
287 }
288}
289
290fn sign4(v: u16) -> i64 {
292 let x = v as i64;
293 (x << 60) >> 60
294}
295
296fn u16s(bytes: &[u8]) -> Vec<u16> {
297 bytes
298 .as_chunks::<2>()
299 .0
300 .iter()
301 .map(|c| u16::from_le_bytes([c[0], c[1]]))
302 .collect()
303}
304
305fn payload_units(bytes: &[u8], off: usize) -> usize {
307 let units = u16s(bytes);
308 let unit = |i: usize| -> u16 { units.get(i).copied().unwrap_or(0) };
309 match unit(off) {
310 0x0100 => {
311 let size = unit(off + 1) as usize;
312 4 + size * 2
313 }
314 0x0200 => {
315 let size = unit(off + 1) as usize;
316 2 + size * 4
317 }
318 0x0300 => {
319 let width = unit(off + 1) as usize;
320 let size = (unit(off + 2) as usize) | ((unit(off + 3) as usize) << 16);
321 4 + (size * width).div_ceil(2)
322 }
323 _ => 0,
324 }
325}
326
327fn parse_payload(bytes: &[u8], off: usize) -> Option<Payload> {
328 let units = u16s(bytes);
329 let unit = |i: usize| -> u16 { units.get(i).copied().unwrap_or(0) };
330 match unit(off) {
331 0x0100 => {
332 let size = unit(off + 1) as usize;
333 let first_key = (unit(off + 2) as i32) | ((unit(off + 3) as i32) << 16);
334 let mut targets = Vec::with_capacity(size);
335 for i in 0..size {
336 let t = (unit(off + 4 + 2 * i) as i32) | ((unit(off + 5 + 2 * i) as i32) << 16);
337 targets.push(t);
338 }
339 Some(Payload::Packed { first_key, targets })
340 }
341 0x0200 => {
342 let size = unit(off + 1) as usize;
343 let mut pairs = Vec::with_capacity(size);
344 for i in 0..size {
345 let k = (unit(off + 2 + 2 * i) as i32) | ((unit(off + 3 + 2 * i) as i32) << 16);
346 let t = (unit(off + 2 + 2 * size + 2 * i) as i32)
347 | ((unit(off + 3 + 2 * size + 2 * i) as i32) << 16);
348 pairs.push((k, t));
349 }
350 Some(Payload::Sparse { pairs })
351 }
352 0x0300 => {
353 let elem_width = unit(off + 1);
354 let size = (unit(off + 2) as u32) | ((unit(off + 3) as u32) << 16);
355 let n = size as usize * elem_width as usize;
356 let start = (off + 4) * 2;
357 let data = if start + n <= bytes.len() {
358 bytes[start..start + n].to_vec()
359 } else {
360 Vec::new()
361 };
362 Some(Payload::ArrayData {
363 elem_width,
364 size,
365 data,
366 })
367 }
368 _ => None,
369 }
370}
371
372fn format_units(fmt: u8) -> u32 {
374 match fmt {
375 10..=12 => 1,
376 20..=23 => 2,
377 30 | 31 | 32 | 35 | 3 => 3,
378 45 | 4 => 4,
379 51 | 5 => 5,
380 _ => 1,
381 }
382}
383
384fn decode_one(units: &[u16], pc: usize) -> (u32, InsnKind) {
387 let op = (units[pc] & 0xff) as u8;
388 let aa = units[pc] >> 8; let u = |i: usize| -> u16 { units.get(i).copied().unwrap_or(0) };
390 let i32of = |i: usize| -> i32 {
391 let lo = u(i) as u32;
392 let hi = u(i + 1) as u32;
393 (lo | (hi << 16)) as i32
394 };
395
396 let a4 = (units[pc] >> 12) & 0xf;
398 let b4 = (units[pc] >> 8) & 0xf;
399
400 let mk = match op {
401 0x00 => {
403 return (1, InsnKind::Nop);
406 }
407 0x01 | 0x04 | 0x07 => InsnKind::Move { dst: b4, src: a4 },
408 0x02 | 0x05 | 0x08 => {
409 return (
410 2,
411 InsnKind::Move {
412 dst: aa,
413 src: u(pc + 1),
414 },
415 );
416 }
417 0x03 | 0x06 | 0x09 => {
418 return (
419 3,
420 InsnKind::Move {
421 dst: u(pc + 1),
422 src: u(pc + 2),
423 },
424 );
425 }
426 0x0a..=0x0c => InsnKind::MoveResult { dst: aa },
427 0x0d => InsnKind::MoveException { dst: aa },
428 0x0e => InsnKind::ReturnVoid,
429 0x0f..=0x11 => InsnKind::Return { src: aa },
430
431 0x12 => InsnKind::Const {
433 dst: b4,
434 val: sign4(a4),
435 wide: false,
436 },
437 0x13 | 0x16 => {
438 return (
439 2,
440 InsnKind::Const {
441 dst: aa,
442 val: (u(pc + 1) as i16) as i64,
443 wide: op == 0x16,
444 },
445 );
446 }
447 0x14 | 0x17 => {
448 return (
449 3,
450 InsnKind::Const {
451 dst: aa,
452 val: i32of(pc + 1) as i64,
453 wide: op == 0x17,
454 },
455 );
456 }
457 0x15 => {
458 return (
459 2,
460 InsnKind::Const {
461 dst: aa,
462 val: ((u(pc + 1) as u32) << 16) as i32 as i64,
463 wide: false,
464 },
465 );
466 }
467 0x18 => {
468 let lo = u64::from(i32of(pc + 1) as u32);
469 let hi = u64::from(i32of(pc + 3) as u32);
470 return (
471 5,
472 InsnKind::Const {
473 dst: aa,
474 val: (lo | (hi << 32)) as i64,
475 wide: true,
476 },
477 );
478 }
479 0x19 => {
480 return (
481 2,
482 InsnKind::Const {
483 dst: aa,
484 val: ((u(pc + 1) as u64) << 48) as i64,
485 wide: true,
486 },
487 );
488 }
489 0x1a | 0x1b => {
490 let idx = if op == 0x1b {
491 (u(pc + 1) as u32) | ((u(pc + 2) as u32) << 16)
492 } else {
493 u(pc + 1) as u32
494 };
495 return (
496 if op == 0x1b { 3 } else { 2 },
497 InsnKind::ConstString {
498 dst: aa,
499 str_idx: idx,
500 },
501 );
502 }
503 0x1c => {
504 return (
505 2,
506 InsnKind::ConstClass {
507 dst: aa,
508 type_idx: u(pc + 1) as u32,
509 },
510 );
511 }
512
513 0x1d => InsnKind::MonitorEnter { reg: aa },
515 0x1e => InsnKind::MonitorExit { reg: aa },
516 0x1f => {
517 return (
518 2,
519 InsnKind::CheckCast {
520 reg: aa,
521 type_idx: u(pc + 1) as u32,
522 },
523 );
524 }
525 0x20 => {
526 return (
527 2,
528 InsnKind::InstanceOf {
529 dst: b4,
530 src: a4,
531 type_idx: u(pc + 1) as u32,
532 },
533 );
534 }
535 0x21 => InsnKind::ArrayLength { dst: b4, src: a4 },
536 0x22 => {
537 return (
538 2,
539 InsnKind::NewInstance {
540 dst: aa,
541 type_idx: u(pc + 1) as u32,
542 },
543 );
544 }
545 0x23 => {
546 return (
547 2,
548 InsnKind::NewArray {
549 dst: b4,
550 size: a4,
551 type_idx: u(pc + 1) as u32,
552 },
553 );
554 }
555 0x24 | 0x25 => {
556 let (regs, idx) = if op == 0x24 {
558 let g = b4;
559 let cnt = a4;
560 let second = u(pc + 2);
561 let mut regs: Vec<u16> = Vec::with_capacity(cnt as usize);
562 for i in 0..cnt {
563 let shift = 4 * i;
564 let r = if i == 4 { g } else { (second >> shift) & 0xf };
565 regs.push(r);
566 }
567 (regs, u(pc + 1) as u32)
568 } else {
569 let cnt = aa;
570 let first = u(pc + 2);
571 ((first..first + cnt).collect(), u(pc + 1) as u32)
572 };
573 return (
574 3,
575 InsnKind::FilledNewArray {
576 regs,
577 type_idx: idx,
578 },
579 );
580 }
581 0x26 => {
582 let off = i32of(pc + 1);
583 return (
584 3,
585 InsnKind::FillArrayData {
586 reg: aa,
587 payload_pc: (pc as i64 + off as i64) as u32,
588 },
589 );
590 }
591
592 0x27 => InsnKind::Throw { reg: aa },
594 0x28 => {
595 return (
596 1,
597 InsnKind::Goto {
598 target: (pc as i64 + aa as i8 as i64) as u32,
599 },
600 );
601 }
602 0x29 => {
603 return (
604 2,
605 InsnKind::Goto {
606 target: (pc as i64 + u(pc + 1) as i16 as i64) as u32,
607 },
608 );
609 }
610 0x2a => {
611 return (
612 3,
613 InsnKind::Goto {
614 target: (pc as i64 + i32of(pc + 1) as i64) as u32,
615 },
616 );
617 }
618 0x2b => {
619 let off = i32of(pc + 1);
620 return (
621 3,
622 InsnKind::PackedSwitch {
623 reg: aa,
624 payload_pc: (pc as i64 + off as i64) as u32,
625 },
626 );
627 }
628 0x2c => {
629 let off = i32of(pc + 1);
630 return (
631 3,
632 InsnKind::SparseSwitch {
633 reg: aa,
634 payload_pc: (pc as i64 + off as i64) as u32,
635 },
636 );
637 }
638 0x2d..=0x31 => {
639 let kind = match op {
640 0x2d => CmpKind::CmplF,
641 0x2e => CmpKind::CmpgF,
642 0x2f => CmpKind::CmplD,
643 0x30 => CmpKind::CmpgD,
644 _ => CmpKind::CmpJ,
645 };
646 let second = u(pc + 1);
647 return (
648 2,
649 InsnKind::Cmp {
650 dst: aa,
651 a: (second & 0xff),
652 b: (second >> 8),
653 kind,
654 },
655 );
656 }
657 0x32..=0x37 => {
658 let opkind = match op {
659 0x32 => CmpOp::Eq,
660 0x33 => CmpOp::Ne,
661 0x34 => CmpOp::Lt,
662 0x35 => CmpOp::Ge,
663 0x36 => CmpOp::Gt,
664 _ => CmpOp::Le,
665 };
666 let off = u(pc + 1) as i16 as i64 + pc as i64;
667 return (
668 2,
669 InsnKind::If {
670 op: opkind,
671 a: b4,
672 b: a4,
673 z: false,
674 target: off as u32,
675 },
676 );
677 }
678 0x38..=0x3d => {
679 let opkind = match op {
680 0x38 => CmpOp::Eq,
681 0x39 => CmpOp::Ne,
682 0x3a => CmpOp::Lt,
683 0x3b => CmpOp::Ge,
684 0x3c => CmpOp::Gt,
685 _ => CmpOp::Le,
686 };
687 let off = u(pc + 1) as i16 as i64 + pc as i64;
688 return (
689 2,
690 InsnKind::If {
691 op: opkind,
692 a: aa,
693 b: 0,
694 z: true,
695 target: off as u32,
696 },
697 );
698 }
699
700 0x44..=0x51 => {
702 let second = u(pc + 1);
703 let r1 = aa;
704 let r2 = second & 0xff;
705 let r3 = second >> 8;
706 let is_load = op <= 0x4a;
707 let ty = match op {
708 0x44 | 0x4b => 'I',
709 0x45 | 0x4c => 'J',
710 0x46 | 0x4d => 'L',
711 0x47 | 0x4e => 'Z',
712 0x48 | 0x4f => 'B',
713 0x49 | 0x50 => 'C',
714 _ => 'S',
715 };
716 if is_load {
718 return (
719 2,
720 InsnKind::AGet {
721 dst: r1,
722 array: r2,
723 index: r3,
724 ty,
725 },
726 );
727 } else {
728 return (
729 2,
730 InsnKind::APut {
731 value: r1,
732 array: r2,
733 index: r3,
734 ty,
735 },
736 );
737 }
738 }
739
740 0x52..=0x58 => {
742 return (
743 2,
744 InsnKind::IGet {
745 dst: b4,
746 obj: a4,
747 field_idx: u(pc + 1) as u32,
748 },
749 );
750 }
751 0x59..=0x5f => {
752 return (
753 2,
754 InsnKind::IPut {
755 value: b4,
756 obj: a4,
757 field_idx: u(pc + 1) as u32,
758 },
759 );
760 }
761
762 0x60..=0x66 => {
764 return (
765 2,
766 InsnKind::SGet {
767 dst: aa,
768 field_idx: u(pc + 1) as u32,
769 },
770 );
771 }
772 0x67..=0x6d => {
773 return (
774 2,
775 InsnKind::SPut {
776 value: aa,
777 field_idx: u(pc + 1) as u32,
778 },
779 );
780 }
781
782 0xfc | 0xfd => {
784 let cs_idx = u(pc + 1) as u32;
785 let regs = if op == 0xfd {
786 let cnt = aa;
787 let first = u(pc + 2);
788 (first..first + cnt).collect()
789 } else {
790 let cnt = a4;
791 let g = b4;
792 let second = u(pc + 2);
793 let mut regs: Vec<u16> = Vec::with_capacity(cnt as usize);
794 for i in 0..cnt {
795 let r = if i == 4 { g } else { (second >> (4 * i)) & 0xf };
796 regs.push(r);
797 }
798 regs
799 };
800 return (
801 3,
802 InsnKind::InvokeCustom {
803 call_site_idx: cs_idx,
804 regs,
805 },
806 );
807 }
808
809 0x6e..=0x72 | 0x74..=0x78 => {
811 let kind = match op {
812 0x6e | 0x74 => InvokeKind::Virtual,
813 0x6f | 0x75 => InvokeKind::Super,
814 0x70 | 0x76 => InvokeKind::Direct,
815 0x71 | 0x77 => InvokeKind::Static,
816 _ => InvokeKind::Interface,
817 };
818 let is_range = matches!(op, 0x74..=0x78);
819 let idx = u(pc + 1) as u32;
820 let regs = if is_range {
821 let cnt = aa;
822 let first = u(pc + 2);
823 (first..first + cnt).collect()
824 } else {
825 let cnt = a4;
826 let g = b4;
827 let second = u(pc + 2);
828 let mut regs: Vec<u16> = Vec::with_capacity(cnt as usize);
829 for i in 0..cnt {
830 let r = if i == 4 { g } else { (second >> (4 * i)) & 0xf };
831 regs.push(r);
832 }
833 regs
834 };
835 return (
836 3,
837 InsnKind::Invoke {
838 kind,
839 regs,
840 method_idx: idx,
841 },
842 );
843 }
844 0xfa | 0xfb => {
845 let kind = InvokeKind::Polymorphic;
848 let idx = u(pc + 1) as u32;
849 let regs = if op == 0xfb {
850 let cnt = aa;
851 let first = u(pc + 3);
852 (first..first + cnt).collect()
853 } else {
854 let cnt = a4;
855 let g = b4;
856 let second = u(pc + 2);
857 let mut regs: Vec<u16> = Vec::with_capacity(cnt as usize);
858 for i in 0..cnt {
859 let r = if i == 4 { g } else { (second >> (4 * i)) & 0xf };
860 regs.push(r);
861 }
862 regs
863 };
864 return (
865 4,
866 InsnKind::Invoke {
867 kind,
868 regs,
869 method_idx: idx,
870 },
871 );
872 }
873 0xfe => {
874 return (
875 2,
876 InsnKind::ConstMethodHandle {
877 dst: aa,
878 handle_idx: u(pc + 1) as u32,
879 },
880 );
881 }
882 0xff => {
883 return (
884 2,
885 InsnKind::ConstMethodType {
886 dst: aa,
887 proto_idx: u(pc + 1) as u32,
888 },
889 );
890 }
891
892 0x7b..=0x8f => {
894 let (opkind, from, to) = match op {
895 0x7b => (UnArith::Neg, 'I', 'I'),
896 0x7c => (UnArith::Not, 'I', 'I'),
897 0x7d => (UnArith::Neg, 'J', 'J'),
898 0x7e => (UnArith::Not, 'J', 'J'),
899 0x7f => (UnArith::Neg, 'F', 'F'),
900 0x80 => (UnArith::Neg, 'D', 'D'),
901 0x81 => (UnArith::Conv, 'I', 'J'),
902 0x82 => (UnArith::Conv, 'I', 'F'),
903 0x83 => (UnArith::Conv, 'I', 'D'),
904 0x84 => (UnArith::Conv, 'J', 'I'),
905 0x85 => (UnArith::Conv, 'J', 'F'),
906 0x86 => (UnArith::Conv, 'J', 'D'),
907 0x87 => (UnArith::Conv, 'F', 'I'),
908 0x88 => (UnArith::Conv, 'F', 'J'),
909 0x89 => (UnArith::Conv, 'F', 'D'),
910 0x8a => (UnArith::Conv, 'D', 'I'),
911 0x8b => (UnArith::Conv, 'D', 'J'),
912 0x8c => (UnArith::Conv, 'D', 'F'),
913 0x8d => (UnArith::Conv, 'I', 'B'),
914 0x8e => (UnArith::Conv, 'I', 'C'),
915 _ => (UnArith::Conv, 'I', 'S'),
916 };
917 InsnKind::Un {
918 dst: b4,
919 src: a4,
920 op: opkind,
921 from,
922 to,
923 }
924 }
925
926 0x90..=0xaf => {
928 let second = u(pc + 1);
929 let (aop, ty) = bin3(op - 0x90);
930 return (
931 2,
932 InsnKind::Bin {
933 op: aop,
934 dst: aa,
935 a: (second & 0xff),
936 b: (second >> 8),
937 ty,
938 },
939 );
940 }
941
942 0xb0..=0xcf => {
944 let (aop, ty) = bin3(op - 0xb0);
945 InsnKind::Bin {
946 op: aop,
947 dst: b4,
948 a: b4,
949 b: a4,
950 ty,
951 }
952 }
953
954 0xd0..=0xd7 => {
956 let (aop, rsub) = binlit16(op - 0xd0);
957 return (
958 2,
959 InsnKind::BinLit {
960 op: aop,
961 dst: b4,
962 a: a4,
963 lit: u(pc + 1) as i16 as i32,
964 rsub,
965 },
966 );
967 }
968 0xd8..=0xe2 => {
969 let (aop, rsub) = binlit8(op - 0xd8);
970 let second = u(pc + 1);
971 let cc = (second >> 8) as i8;
972 return (
973 2,
974 InsnKind::BinLit {
975 op: aop,
976 dst: aa,
977 a: (second & 0xff),
978 lit: cc as i32,
979 rsub,
980 },
981 );
982 }
983
984 _ => InsnKind::Unknown,
986 };
987
988 (1, mk)
990}
991
992fn bin3(off: u8) -> (ArithOp, char) {
996 const OPS8: [ArithOp; 8] = [
997 ArithOp::Add,
998 ArithOp::Sub,
999 ArithOp::Mul,
1000 ArithOp::Div,
1001 ArithOp::Rem,
1002 ArithOp::And,
1003 ArithOp::Or,
1004 ArithOp::Xor,
1005 ];
1006 const OPS5: [ArithOp; 5] = [
1007 ArithOp::Add,
1008 ArithOp::Sub,
1009 ArithOp::Mul,
1010 ArithOp::Div,
1011 ArithOp::Rem,
1012 ];
1013 match off {
1014 0..=7 => (OPS8[off as usize], 'I'),
1015 8..=10 => (
1016 [ArithOp::Shl, ArithOp::Shr, ArithOp::Ushr][(off - 8) as usize],
1017 'I',
1018 ),
1019 11..=18 => (OPS8[(off - 11) as usize], 'J'),
1020 19..=21 => (
1021 [ArithOp::Shl, ArithOp::Shr, ArithOp::Ushr][(off - 19) as usize],
1022 'J',
1023 ),
1024 22..=26 => (OPS5[(off - 22) as usize], 'F'),
1025 _ => (OPS5[(off - 27) as usize], 'D'),
1026 }
1027}
1028
1029fn binlit16(off: u8) -> (ArithOp, bool) {
1031 match off {
1032 0 => (ArithOp::Add, false),
1033 1 => (ArithOp::Sub, true), 2 => (ArithOp::Mul, false),
1035 3 => (ArithOp::Div, false),
1036 4 => (ArithOp::Rem, false),
1037 5 => (ArithOp::And, false),
1038 6 => (ArithOp::Or, false),
1039 _ => (ArithOp::Xor, false),
1040 }
1041}
1042
1043fn binlit8(off: u8) -> (ArithOp, bool) {
1045 match off {
1046 0 => (ArithOp::Add, false),
1047 1 => (ArithOp::Sub, true),
1048 2 => (ArithOp::Mul, false),
1049 3 => (ArithOp::Div, false),
1050 4 => (ArithOp::Rem, false),
1051 5 => (ArithOp::And, false),
1052 6 => (ArithOp::Or, false),
1053 7 => (ArithOp::Xor, false),
1054 8 => (ArithOp::Shl, false),
1055 9 => (ArithOp::Shr, false),
1056 _ => (ArithOp::Ushr, false),
1057 }
1058}
1059
1060pub fn opcode_units(op: u8) -> u32 {
1065 match op {
1066 0x00 => 1,
1067 0x01 | 0x04 | 0x07 => 1,
1068 0x02 | 0x05 | 0x08 => 2,
1069 0x03 | 0x06 | 0x09 => 3,
1070 0x0a..=0x12 => 1,
1071 0x13 | 0x15 | 0x16 | 0x19 => 2,
1072 0x14 | 0x17 => 3,
1073 0x18 => 5,
1074 0x1a | 0x1c => 2,
1075 0x1b => 3,
1076 0x1d | 0x1e | 0x21 | 0x27 | 0x28 => 1,
1077 0x1f | 0x20 | 0x22 | 0x23 => 2,
1078 0x24..=0x26 => 3,
1079 0x29 => 2,
1080 0x2a => 3,
1081 0x2b | 0x2c => 2,
1082 0x2d..=0x37 => 2,
1083 0x38..=0x43 => 2,
1084 0x44..=0x6d => 2,
1085 0x6e..=0x72 => 3,
1086 0x73 => 1,
1087 0x74..=0x78 => 3,
1088 0x79 | 0x7a => 1,
1089 0x7b..=0x8a => 1,
1090 0x8b..=0x8f => 2,
1091 0x90..=0xaf => 2,
1092 0xb0..=0xcf => 1,
1093 0xd0..=0xe2 => 2,
1094 0xfc | 0xfd => 3,
1097 0xfe | 0xff => 2,
1098 _ => 1,
1101 }
1102}
1103
1104#[inline]
1105fn unit_at(bytes: &[u8], i: usize) -> u16 {
1106 if 2 * i + 1 < bytes.len() {
1107 u16::from_le_bytes([bytes[2 * i], bytes[2 * i + 1]])
1108 } else {
1109 0
1110 }
1111}
1112
1113#[inline]
1114fn units_len(bytes: &[u8]) -> usize {
1115 bytes.len() / 2
1116}
1117
1118pub fn scan_instructions<F: FnMut(u8, usize, &[u8])>(bytes: &[u8], f: &mut F) {
1124 let n = units_len(bytes);
1125 let mut pc = 0usize;
1126 while pc < n {
1127 let first = unit_at(bytes, pc);
1128 if first == 0x0100 || first == 0x0200 || first == 0x0300 {
1129 let size = payload_units_from_bytes(bytes, pc);
1130 pc += if size == 0 { 1 } else { size };
1131 continue;
1132 }
1133 let op = (first & 0xff) as u8;
1134 f(op, pc, bytes);
1135 pc += opcode_units(op).max(1) as usize;
1136 }
1137}
1138
1139fn payload_units_from_bytes(bytes: &[u8], pc: usize) -> usize {
1140 let unit = |i: usize| -> u16 { unit_at(bytes, i) };
1141 match unit(pc) {
1142 0x0100 => 4 + unit(pc + 1) as usize * 2,
1143 0x0200 => 2 + unit(pc + 1) as usize * 4,
1144 0x0300 => {
1145 let width = unit(pc + 1) as usize;
1146 let size = (unit(pc + 2) as usize) | ((unit(pc + 3) as usize) << 16);
1147 4 + (size * width).div_ceil(2)
1148 }
1149 _ => 0,
1150 }
1151}
1152
1153pub fn decode_all(bytes: &[u8]) -> (Vec<Insn>, HashMap<u32, Payload>) {
1155 let units = u16s(bytes);
1156 let mut insns = Vec::new();
1157 let mut payloads = HashMap::new();
1158 let mut pc = 0usize;
1159 while pc < units.len() {
1160 let first = units[pc];
1162 if first == 0x0100 || first == 0x0200 || first == 0x0300 {
1163 let size = payload_units(bytes, pc);
1164 if size == 0 {
1165 insns.push(Insn {
1167 pc: pc as u32,
1168 size: 1,
1169 op: 0,
1170 kind: InsnKind::Nop,
1171 });
1172 pc += 1;
1173 continue;
1174 }
1175 if let Some(p) = parse_payload(bytes, pc) {
1176 payloads.insert(pc as u32, p);
1177 }
1178 pc += size;
1179 continue;
1180 }
1181 let (size, kind) = decode_one(&units, pc);
1182 let size = size.max(1);
1183 insns.push(Insn {
1184 pc: pc as u32,
1185 size,
1186 op: (units[pc] & 0xff) as u8,
1187 kind,
1188 });
1189 pc += size as usize;
1190 }
1191 (insns, payloads)
1192}
1193
1194pub fn op_name(op: u8) -> &'static str {
1196 match op {
1197 0x00 => "nop",
1198 0x01 => "move",
1199 0x02 => "move/from16",
1200 0x03 => "move/16",
1201 0x04 => "move-wide",
1202 0x05 => "move-wide/from16",
1203 0x06 => "move-wide/16",
1204 0x07 => "move-object",
1205 0x08 => "move-object/from16",
1206 0x09 => "move-object/16",
1207 0x0a => "move-result",
1208 0x0b => "move-result-wide",
1209 0x0c => "move-result-object",
1210 0x0d => "move-exception",
1211 0x0e => "return-void",
1212 0x0f => "return",
1213 0x10 => "return-wide",
1214 0x11 => "return-object",
1215 0x12 => "const/4",
1216 0x13 => "const/16",
1217 0x14 => "const",
1218 0x15 => "const/high16",
1219 0x16 => "const-wide/16",
1220 0x17 => "const-wide/32",
1221 0x18 => "const-wide",
1222 0x19 => "const-wide/high16",
1223 0x1a => "const-string",
1224 0x1b => "const-string/jumbo",
1225 0x1c => "const-class",
1226 0x1d => "monitor-enter",
1227 0x1e => "monitor-exit",
1228 0x1f => "check-cast",
1229 0x20 => "instance-of",
1230 0x21 => "array-length",
1231 0x22 => "new-instance",
1232 0x23 => "new-array",
1233 0x24 => "filled-new-array",
1234 0x25 => "filled-new-array/range",
1235 0x26 => "fill-array-data",
1236 0x27 => "throw",
1237 0x28 => "goto",
1238 0x29 => "goto/16",
1239 0x2a => "goto/32",
1240 0x2b => "packed-switch",
1241 0x2c => "sparse-switch",
1242 0x2d..=0x31 => "cmp",
1243 0x32..=0x3d => "if",
1244 0x44..=0x4a => "aget",
1245 0x4b..=0x51 => "aput",
1246 0x52..=0x58 => "iget",
1247 0x59..=0x5f => "iput",
1248 0x60..=0x66 => "sget",
1249 0x67..=0x6d => "sput",
1250 0x6e..=0x72 => "invoke",
1251 0x74..=0x78 => "invoke/range",
1252 0x7b..=0x8f => "unop",
1253 0x90..=0xaf => "binop",
1254 0xb0..=0xcf => "binop/2addr",
1255 0xd0..=0xd7 => "binop/lit16",
1256 0xd8..=0xe2 => "binop/lit8",
1257 0xfa | 0xfb => "invoke-polymorphic",
1258 0xfc | 0xfd => "invoke-custom",
1259 0xfe => "const-method-handle",
1260 0xff => "const-method-type",
1261 _ => "unknown",
1262 }
1263}
1264
1265#[allow(dead_code)]
1266fn unused_format_units() {
1267 let _ = format_units(10);
1268 let _ = Cursor::new(&[]);
1269}