pydis/opcode/
mod.rs

1use std::fmt::{self, Debug};
2
3pub use num_traits::FromPrimitive;
4pub use num_traits::ToPrimitive;
5
6use self::py27::Mnemonic;
7
8pub mod py27;
9
10#[derive(Debug, Clone, PartialEq, Eq)]
11pub struct Instruction<O: Opcode> {
12    pub opcode: O,
13    pub arg: Option<u16>,
14}
15
16#[macro_export]
17macro_rules! Instr {
18    ($opcode:expr) => {
19        ::pydis::opcode::Instruction {
20            opcode: $opcode,
21            arg: None,
22        }
23    };
24    ($opcode:expr, $arg:expr) => {
25        ::pydis::opcode::Instruction {
26            opcode: $opcode,
27            arg: Some($arg),
28        }
29    };
30}
31
32impl<O: Opcode + Debug> fmt::Display for Instruction<O> {
33    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
34        write!(f, "{:?}", self.opcode)?;
35
36        if let Some(arg) = self.arg {
37            write!(f, " {}", arg)?;
38        }
39
40        Ok(())
41    }
42}
43
44impl<O: Opcode> Instruction<O> {
45    /// The length of this instruction in bytes
46    pub fn len(&self) -> usize {
47        std::mem::size_of::<u8>()
48            + if let Some(arg) = self.arg.as_ref() {
49                std::mem::size_of_val(arg)
50            } else {
51                0
52            }
53    }
54}
55
56impl<O: Opcode<Mnemonic = py27::Mnemonic>> Instruction<O> {
57    /// How the stack adjusts after this instruction executes. A positive number indicates that N
58    /// elements were pushed to the stack while a negative number indicates the number of elements
59    /// removed from the stack
60    pub fn stack_adjustment_after(&self) -> isize {
61        match self.opcode.mnemonic() {
62            // Meta instructions
63            Mnemonic::STOP_CODE
64            | Mnemonic::NOP
65            | Mnemonic::ROT_TWO
66            | Mnemonic::ROT_THREE
67            | Mnemonic::ROT_FOUR => 0,
68
69            Mnemonic::POP_TOP => -1,
70            Mnemonic::DUP_TOP => 1,
71            Mnemonic::DUP_TOP_TWO => 2,
72            // Unary ops
73            Mnemonic::UNARY_POSITIVE
74            | Mnemonic::UNARY_NEGATIVE
75            | Mnemonic::UNARY_NOT
76            | Mnemonic::UNARY_CONVERT
77            | Mnemonic::UNARY_INVERT
78            | Mnemonic::GET_ITER => 0,
79            // Binary ops
80            Mnemonic::BINARY_POWER
81            | Mnemonic::BINARY_MULTIPLY
82            | Mnemonic::BINARY_DIVIDE
83            | Mnemonic::BINARY_FLOOR_DIVIDE
84            | Mnemonic::BINARY_TRUE_DIVIDE
85            | Mnemonic::BINARY_MODULO
86            | Mnemonic::BINARY_ADD
87            | Mnemonic::BINARY_SUBTRACT
88            | Mnemonic::BINARY_SUBSC
89            | Mnemonic::BINARY_LSHIFT
90            | Mnemonic::BINARY_RSHIFT
91            | Mnemonic::BINARY_AND
92            | Mnemonic::BINARY_XOR
93            | Mnemonic::BINARY_OR => -1,
94            // In-place operations
95            Mnemonic::INPLACE_POWER
96            | Mnemonic::INPLACE_MULTIPLY
97            | Mnemonic::INPLACE_DIVIDE
98            | Mnemonic::INPLACE_FLOOR_DIVIDE
99            | Mnemonic::INPLACE_TRUE_DIVIDE
100            | Mnemonic::INPLACE_MODULO
101            | Mnemonic::INPLACE_ADD
102            | Mnemonic::INPLACE_SUBTRACT
103            | Mnemonic::INPLACE_LSHIFT
104            | Mnemonic::INPLACE_RSHIFT
105            | Mnemonic::INPLACE_AND
106            | Mnemonic::INPLACE_XOR
107            | Mnemonic::INPLACE_OR => -1,
108            // Slice operations
109            Mnemonic::SLICE_0 => 0,
110            Mnemonic::SLICE_1 => -1,
111            Mnemonic::SLICE_2 => -2,
112            Mnemonic::SLICE_3 => -3,
113            Mnemonic::STORE_SLICE_0 => -1,
114            Mnemonic::STORE_SLICE_1 => -2,
115            Mnemonic::STORE_SLICE_2 => -3,
116            Mnemonic::STORE_SLICE_3 => -4,
117            Mnemonic::DELETE_SLICE_0 => -1,
118            Mnemonic::DELETE_SLICE_1 => -2,
119            Mnemonic::DELETE_SLICE_2 => -3,
120            Mnemonic::DELETE_SLICE_3 => -4,
121            Mnemonic::STORE_SUBSCR => -3,
122            Mnemonic::DELETE_SUBSCR => -2,
123            // Misc
124            Mnemonic::PRINT_EXPR => -1,
125            Mnemonic::PRINT_ITEM => -1,
126            Mnemonic::PRINT_ITEM_TO => -2,
127            Mnemonic::PRINT_NEWLINE => 0,
128            Mnemonic::PRINT_NEWLINE_TO => -1,
129            Mnemonic::BREAK_LOOP => 0,
130            Mnemonic::CONTINUE_LOOP => 0,
131            Mnemonic::LIST_APPEND => -1,
132            Mnemonic::LOAD_LOCALS => 1,
133            Mnemonic::RETURN_VALUE => 0,
134            Mnemonic::YIELD_VALUE => 0,
135            Mnemonic::IMPORT_STAR => -1,
136            Mnemonic::EXEC_STMT => -3,
137            Mnemonic::POP_BLOCK => 0,
138            Mnemonic::END_FINALLY => 0,
139            Mnemonic::BUILD_CLASS => -3,
140            // TODO: maybe not right?
141            Mnemonic::SETUP_WITH => 1,
142            Mnemonic::WITH_CLEANUP => {
143                panic!("with_cleanup may require runtime info");
144            }
145            Mnemonic::STORE_NAME => -1,
146            Mnemonic::STORE_FAST => -1,
147            Mnemonic::STORE_DEREF => -1,
148            Mnemonic::SET_ADD => -1,
149            Mnemonic::MAP_ADD => -1,
150            Mnemonic::DELETE_NAME => 0,
151            Mnemonic::UNPACK_SEQUENCE => (self.arg.unwrap() as isize) - 1,
152            Mnemonic::DUP_TOPX => self.arg.unwrap() as isize,
153            Mnemonic::STORE_ATTR => -2,
154            Mnemonic::DELETE_ATTR => -1,
155            Mnemonic::STORE_GLOBAL => -1,
156            Mnemonic::DELETE_GLOBAL => 0,
157            Mnemonic::LOAD_CONST => 1,
158            Mnemonic::LOAD_NAME => 1,
159            Mnemonic::BUILD_TUPLE | Mnemonic::BUILD_LIST | Mnemonic::BUILD_SET => {
160                (self.arg.unwrap() as isize) - 1
161            }
162            Mnemonic::BUILD_MAP => 1,
163            Mnemonic::LOAD_ATTR => 0,
164            Mnemonic::COMPARE_OP => -1,
165            Mnemonic::IMPORT_NAME => -1,
166            Mnemonic::IMPORT_FROM => 1,
167            Mnemonic::JUMP_FORWARD | Mnemonic::JUMP_ABSOLUTE => 0,
168            Mnemonic::POP_JUMP_IF_FALSE | Mnemonic::POP_JUMP_IF_TRUE => -1,
169            Mnemonic::JUMP_IF_FALSE_OR_POP | Mnemonic::JUMP_IF_TRUE_OR_POP => {
170                panic!("JUMP_IF_*_OR_POP requires runtime info");
171            }
172            Mnemonic::FOR_ITER => 1,
173            Mnemonic::LOAD_GLOBAL => 1,
174            Mnemonic::SETUP_LOOP => 0,
175            Mnemonic::SETUP_EXCEPT | Mnemonic::SETUP_FINALLY => {
176                panic!("SETUP_EXCEPT requires runtime info");
177            }
178            Mnemonic::STORE_MAP => -2,
179            Mnemonic::LOAD_FAST => 1,
180            Mnemonic::DELETE_FAST => 0,
181            Mnemonic::LOAD_CLOSURE => 0,
182            Mnemonic::LOAD_DEREF => 1,
183            Mnemonic::RAISE_VARARGS => 0,
184            Mnemonic::CALL_FUNCTION => {
185                let pos_args = self.arg.unwrap() & 0xFF;
186                let kwargs = (self.arg.unwrap() >> 8) & 0xFF;
187                // 1 arg is removed for the callable, 1 is added for the return value
188                -(pos_args as isize + kwargs as isize + 1) + 1
189            }
190            Mnemonic::MAKE_FUNCTION => -1,
191            Mnemonic::MAKE_CLOSURE => 2 + self.arg.unwrap() as isize,
192            Mnemonic::BUILD_SLICE => 1 - (self.arg.unwrap() as isize),
193            Mnemonic::EXTENDED_ARG => panic!("not supported yet"),
194            Mnemonic::CALL_FUNCTION_VAR | Mnemonic::CALL_FUNCTION_KW => {
195                let pos_args = self.arg.unwrap() & 0xFF;
196                let kwargs = (self.arg.unwrap() >> 8) & 0xFF;
197                // 1 arg is removed for the callable and additional positional args, 1 is added for the return value
198                -(pos_args as isize + kwargs as isize + 2) + 1
199            }
200            Mnemonic::CALL_FUNCTION_VAR_KW => {
201                let pos_args = self.arg.unwrap() & 0xFF;
202                let kwargs = (self.arg.unwrap() >> 8) & 0xFF;
203                // 1 arg is removed for the callable and additional positional+kw args, 1 is added for the return value
204                -(pos_args as isize + kwargs as isize + 3) + 1
205            }
206        }
207    }
208}
209
210/// Trait that provides convenience routines for opcode properties such as whether
211/// or not it has an argument, is a jump, etc.
212pub trait Opcode: From<Self::Mnemonic> + Send + Sync + FromPrimitive + ToPrimitive + Copy + Clone + Debug {
213    type Mnemonic;
214
215    /// Whether or not this opcode has an argument
216    fn has_arg(&self) -> bool;
217
218    /// Whether or not this opcode has an extended argument
219    fn has_extended_arg(&self) -> bool;
220
221    /// Whether or not this opcode has a constant parameter
222    fn has_const(&self) -> bool;
223
224    /// Whether or not this opcode is a boolean operation
225    fn has_comp(&self) -> bool;
226
227    /// Whether or not this opcode is any kind of instruction which may jump
228    fn is_jump(&self) -> bool {
229        self.is_relative_jump() || self.is_absolute_jump()
230    }
231
232    fn is_other_conditional_jump(&self) -> bool;
233
234    /// Whether or not this opcode has a relative jump target
235    fn is_relative_jump(&self) -> bool;
236
237    /// Whether or not this opcode has an absolute jump target
238    fn is_absolute_jump(&self) -> bool;
239
240    /// Whether or not this opcode is a conditional jump
241    fn is_conditional_jump(&self) -> bool;
242
243    /// Whether or not this opcode accesses an attribute by name
244    fn has_name(&self) -> bool;
245
246    /// Whether or not this opcode accesses a local variable
247    fn has_local(&self) -> bool;
248
249    /// Whether or not this opcode accesses a free variable
250    fn has_free(&self) -> bool;
251
252    fn mnemonic(&self) -> Self::Mnemonic;
253}