Skip to main content

idakit_sys/
instruction.rs

1//! Instruction-decode sentinels shared with the generated decode bridge: control-flow flags and
2//! operand access bits. The scalar sentinels (`MAX_OPS`, `OP_*`, `REG_NONE`, `FLOW_*`) are
3//! generated by `idakit-sys-codegen`'s `instruction` domain spec.
4
5use bitflags::bitflags;
6
7bitflags! {
8    /// `InstructionData::flow` control-flow bits for a decoded instruction: [`crate::FLOW_CALL`]/
9    /// [`crate::FLOW_RET`]/[`crate::FLOW_JUMP`]/[`crate::FLOW_INDIRECT`]/[`crate::FLOW_STOPS`].
10    ///
11    /// `FLOW_*` is idakit's own facade sentinel, not a raw SDK value; accepts any bit pattern
12    /// (`from_bits_retain`), since `InstructionData::flow` is a raw `u8` field the C++ decoder
13    /// writes.
14    #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
15    pub struct FlowFlags: u8 {
16        /// The instruction is a call.
17        const CALL = crate::FLOW_CALL;
18        /// The instruction returns.
19        const RET = crate::FLOW_RET;
20        /// The instruction jumps.
21        const JUMP = crate::FLOW_JUMP;
22        /// The transfer target is indirect, not statically known.
23        const INDIRECT = crate::FLOW_INDIRECT;
24        /// Sequential flow stops after this instruction.
25        const STOPS = crate::FLOW_STOPS;
26    }
27}
28
29bitflags! {
30    /// `OperandData::access` bits for a decoded operand: `bit0` read, `bit1` written.
31    ///
32    /// Accepts any bit pattern (`from_bits_retain`), since `OperandData::access` is a raw `u8`
33    /// field the C++ decoder writes.
34    #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
35    pub struct OperandAccess: u8 {
36        /// The operand is read.
37        const READ = 1;
38        /// The operand is written.
39        const WRITTEN = 2;
40    }
41}
42
43#[cfg(test)]
44mod tests {
45    use assert2::assert;
46    use proptest::prelude::*;
47    use rstest::rstest;
48
49    use super::*;
50
51    #[rstest]
52    #[case::call(FlowFlags::CALL, crate::FLOW_CALL)]
53    #[case::ret(FlowFlags::RET, crate::FLOW_RET)]
54    #[case::jump(FlowFlags::JUMP, crate::FLOW_JUMP)]
55    #[case::indirect(FlowFlags::INDIRECT, crate::FLOW_INDIRECT)]
56    #[case::stops(FlowFlags::STOPS, crate::FLOW_STOPS)]
57    fn flags_pin_the_generated_facade_values(#[case] flag: FlowFlags, #[case] raw: u8) {
58        assert!(flag.bits() == raw);
59    }
60
61    #[rstest]
62    #[case::read(OperandAccess::READ, 1)]
63    #[case::written(OperandAccess::WRITTEN, 2)]
64    fn operand_access_flags_pin_the_raw_values(#[case] flag: OperandAccess, #[case] raw: u8) {
65        assert!(flag.bits() == raw);
66    }
67
68    proptest! {
69        #[test]
70        fn flow_flags_from_bits_retain_round_trips_every_byte(raw: u8) {
71            prop_assert_eq!(FlowFlags::from_bits_retain(raw).bits(), raw);
72        }
73
74        #[test]
75        fn flow_flags_union_and_intersection_are_raw_bitwise_ops(a: u8, b: u8) {
76            let (fa, fb) = (FlowFlags::from_bits_retain(a), FlowFlags::from_bits_retain(b));
77            prop_assert_eq!((fa | fb).bits(), a | b);
78            prop_assert_eq!((fa & fb).bits(), a & b);
79        }
80
81        #[test]
82        fn operand_access_from_bits_retain_round_trips_every_byte(raw: u8) {
83            prop_assert_eq!(OperandAccess::from_bits_retain(raw).bits(), raw);
84        }
85
86        #[test]
87        fn operand_access_union_and_intersection_are_raw_bitwise_ops(a: u8, b: u8) {
88            let (fa, fb) = (OperandAccess::from_bits_retain(a), OperandAccess::from_bits_retain(b));
89            prop_assert_eq!((fa | fb).bits(), a | b);
90            prop_assert_eq!((fa & fb).bits(), a & b);
91        }
92    }
93}