Skip to main content

revm_bytecode/
opcode.rs

1//! EVM opcode definitions and utilities. It contains opcode information and utilities to work with opcodes.
2
3#[cfg(feature = "parse")]
4pub mod parse;
5
6use core::{fmt, ptr::NonNull};
7
8/// An EVM opcode
9///
10/// This is always a valid opcode, as declared in the [`opcode`][self] module or the
11/// [`OPCODE_INFO`] constant.
12#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
13#[repr(transparent)]
14pub struct OpCode(u8);
15
16impl fmt::Display for OpCode {
17    /// Formats the opcode as a string
18    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
19        let n = self.get();
20        if let Some(val) = OPCODE_INFO[n as usize] {
21            f.write_str(val.name())
22        } else {
23            write!(f, "UNKNOWN(0x{n:02X})")
24        }
25    }
26}
27
28impl OpCode {
29    /// Instantiates a new opcode from a u8.
30    ///
31    /// Returns None if the opcode is not valid.
32    #[inline]
33    pub const fn new(opcode: u8) -> Option<Self> {
34        match OPCODE_INFO[opcode as usize] {
35            Some(_) => Some(Self(opcode)),
36            None => None,
37        }
38    }
39
40    /// Instantiates a new opcode from a u8.
41    #[inline]
42    pub const fn new_or_unknown(opcode: u8) -> Self {
43        Self(opcode)
44    }
45
46    /// Returns true if the opcode is a jump destination.
47    #[inline]
48    pub const fn is_jumpdest(&self) -> bool {
49        Self::is_jumpdest_by_op(self.0)
50    }
51
52    /// Takes a u8 and returns true if it is a jump destination.
53    #[inline]
54    pub const fn is_jumpdest_by_op(opcode: u8) -> bool {
55        opcode == JUMPDEST
56    }
57
58    /// Returns true if the opcode is a legacy jump instruction.
59    #[inline]
60    pub const fn is_jump(self) -> bool {
61        Self::is_jump_by_op(self.0)
62    }
63
64    /// Takes a u8 and returns true if it is a jump instruction.
65    #[inline]
66    pub const fn is_jump_by_op(opcode: u8) -> bool {
67        opcode == JUMP
68    }
69
70    /// Returns true if the opcode is a `PUSH1..=PUSH32` instruction.
71    #[inline]
72    pub const fn is_push(self) -> bool {
73        Self::is_push_by_op(self.0)
74    }
75
76    /// Returns true if the opcode is a `PUSH1..=PUSH32` instruction.
77    #[inline]
78    pub const fn is_push_by_op(opcode: u8) -> bool {
79        opcode >= PUSH1 && opcode <= PUSH32
80    }
81
82    /// Instantiates a new opcode from a u8 without checking if it is valid.
83    ///
84    /// # Safety
85    ///
86    /// All code using `Opcode` values assume that they are valid opcodes, so providing an invalid
87    /// opcode may cause undefined behavior.
88    #[inline]
89    #[deprecated = "use new_or_unknown instead"]
90    #[doc(hidden)]
91    pub const unsafe fn new_unchecked(opcode: u8) -> Self {
92        Self(opcode)
93    }
94
95    /// Returns the opcode as a string. This is the inverse of [`parse`](Self::parse).
96    #[doc(alias = "name")]
97    #[inline]
98    pub const fn as_str(self) -> &'static str {
99        self.info().name()
100    }
101
102    /// Returns the opcode name.
103    #[inline]
104    pub const fn name_by_op(opcode: u8) -> &'static str {
105        if let Some(opcode) = Self::new(opcode) {
106            opcode.as_str()
107        } else {
108            "Unknown"
109        }
110    }
111
112    /// Returns the number of input stack elements.
113    #[inline]
114    pub const fn inputs(&self) -> u8 {
115        self.info().inputs()
116    }
117
118    /// Returns the number of output stack elements.
119    #[inline]
120    pub const fn outputs(&self) -> u8 {
121        self.info().outputs()
122    }
123
124    /// Calculates the difference between the number of input and output stack elements.
125    #[inline]
126    pub const fn io_diff(&self) -> i16 {
127        self.info().io_diff()
128    }
129
130    /// Returns the opcode information for the given opcode.
131    /// Check [OpCodeInfo] for more information.
132    #[inline]
133    pub const fn info_by_op(opcode: u8) -> Option<OpCodeInfo> {
134        OPCODE_INFO[opcode as usize]
135    }
136
137    /// Returns the opcode as a usize.
138    #[inline]
139    pub const fn as_usize(&self) -> usize {
140        self.0 as usize
141    }
142
143    /// Returns the opcode information.
144    #[inline]
145    pub const fn info(&self) -> OpCodeInfo {
146        if let Some(info) = OPCODE_INFO[self.0 as usize] {
147            info
148        } else {
149            OpCodeInfo::unknown()
150        }
151    }
152
153    /// Returns the number of both input and output stack elements.
154    ///
155    /// Can be slightly faster than calling `inputs` and `outputs` separately.
156    pub const fn input_output(&self) -> (u8, u8) {
157        let info = self.info();
158        (info.inputs, info.outputs)
159    }
160
161    /// Returns the opcode as a u8.
162    #[inline]
163    pub const fn get(self) -> u8 {
164        self.0
165    }
166
167    /// Returns true if the opcode modifies memory.
168    ///
169    /// <https://docs.rs/revm-interpreter/latest/revm_interpreter/instructions/index.html>
170    ///
171    /// <https://github.com/crytic/evm-opcodes>
172    #[inline]
173    pub const fn modifies_memory(&self) -> bool {
174        matches!(
175            *self,
176            OpCode::EXTCODECOPY
177                | OpCode::MLOAD
178                | OpCode::MSTORE
179                | OpCode::MSTORE8
180                | OpCode::MCOPY
181                | OpCode::KECCAK256
182                | OpCode::CODECOPY
183                | OpCode::CALLDATACOPY
184                | OpCode::RETURNDATACOPY
185                | OpCode::CALL
186                | OpCode::CALLCODE
187                | OpCode::DELEGATECALL
188                | OpCode::STATICCALL
189                | OpCode::LOG0
190                | OpCode::LOG1
191                | OpCode::LOG2
192                | OpCode::LOG3
193                | OpCode::LOG4
194                | OpCode::RETURN
195                | OpCode::REVERT
196                | OpCode::CREATE
197                | OpCode::CREATE2
198        )
199    }
200
201    /// Returns true if the opcode is valid
202    #[inline]
203    pub const fn is_valid(&self) -> bool {
204        OPCODE_INFO[self.0 as usize].is_some()
205    }
206}
207
208impl PartialEq<u8> for OpCode {
209    fn eq(&self, other: &u8) -> bool {
210        self.get().eq(other)
211    }
212}
213
214/// Information about opcode, such as name, and stack inputs and outputs
215#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
216pub struct OpCodeInfo {
217    /// Invariant: `(name_ptr, name_len)` is a [`&'static str`][str].
218    ///
219    /// It is a shorted variant of [`str`] as
220    /// the name length is always less than 256 characters.
221    name_ptr: NonNull<u8>,
222    name_len: u8,
223    /// Stack inputs
224    inputs: u8,
225    /// Stack outputs
226    outputs: u8,
227    /// Number of intermediate bytes
228    immediate_size: u8,
229    /// If the opcode stops execution. aka STOP, RETURN, ..
230    terminating: bool,
231}
232
233// SAFETY: The `NonNull` is just a `&'static str`.
234unsafe impl Send for OpCodeInfo {}
235unsafe impl Sync for OpCodeInfo {}
236
237impl fmt::Debug for OpCodeInfo {
238    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
239        f.debug_struct("OpCodeInfo")
240            .field("name", &self.name())
241            .field("inputs", &self.inputs())
242            .field("outputs", &self.outputs())
243            .field("terminating", &self.is_terminating())
244            .field("immediate_size", &self.immediate_size())
245            .finish()
246    }
247}
248
249impl OpCodeInfo {
250    /// Creates a new opcode info with the given name and default values.
251    pub const fn new(name: &'static str) -> Self {
252        assert!(name.len() < 256, "opcode name is too long");
253        Self {
254            name_ptr: unsafe { NonNull::new_unchecked(name.as_ptr().cast_mut()) },
255            name_len: name.len() as u8,
256            inputs: 0,
257            outputs: 0,
258            terminating: false,
259            immediate_size: 0,
260        }
261    }
262
263    const fn unknown() -> Self {
264        terminating(Self::new("UNKNOWN"))
265    }
266
267    /// Returns the opcode name.
268    #[inline]
269    pub const fn name(&self) -> &'static str {
270        // SAFETY: `self.name_*` can only be initialized with a valid `&'static str`.
271        unsafe {
272            let slice = std::slice::from_raw_parts(self.name_ptr.as_ptr(), self.name_len as usize);
273            core::str::from_utf8_unchecked(slice)
274        }
275    }
276
277    /// Calculates the difference between the number of input and output stack elements.
278    #[inline]
279    pub const fn io_diff(&self) -> i16 {
280        self.outputs as i16 - self.inputs as i16
281    }
282
283    /// Returns the number of input stack elements.
284    #[inline]
285    pub const fn inputs(&self) -> u8 {
286        self.inputs
287    }
288
289    /// Returns the number of output stack elements.
290    #[inline]
291    pub const fn outputs(&self) -> u8 {
292        self.outputs
293    }
294
295    /// Returns whether this opcode terminates execution, e.g. `STOP`, `RETURN`, etc.
296    #[inline]
297    pub const fn is_terminating(&self) -> bool {
298        self.terminating
299    }
300
301    /// Returns the size of the immediate value in bytes.
302    #[inline]
303    pub const fn immediate_size(&self) -> u8 {
304        self.immediate_size
305    }
306}
307
308/// Used for [`OPCODE_INFO`] to set the immediate bytes number in the [`OpCodeInfo`].
309#[inline]
310pub const fn immediate_size(mut op: OpCodeInfo, n: u8) -> OpCodeInfo {
311    op.immediate_size = n;
312    op
313}
314
315/// Used for [`OPCODE_INFO`] to set the terminating flag to true in the [`OpCodeInfo`].
316#[inline]
317pub const fn terminating(mut op: OpCodeInfo) -> OpCodeInfo {
318    op.terminating = true;
319    op
320}
321
322/// Use for [`OPCODE_INFO`] to sets the number of stack inputs and outputs in the [`OpCodeInfo`].
323#[inline]
324pub const fn stack_io(mut op: OpCodeInfo, inputs: u8, outputs: u8) -> OpCodeInfo {
325    op.inputs = inputs;
326    op.outputs = outputs;
327    op
328}
329
330/// Alias for the [`JUMPDEST`] opcode
331pub const NOP: u8 = JUMPDEST;
332
333/// Created all opcodes constants and two maps:
334///  * `OPCODE_INFO` maps opcode number to the opcode info
335///  * `NAME_TO_OPCODE` that maps opcode name to the opcode number.
336macro_rules! opcodes {
337    ($($val:literal => $name:ident => $($modifier:ident $(( $($modifier_arg:expr),* ))?),*);* $(;)?) => {
338        // Constants for each opcode. This also takes care of duplicate names.
339        $(
340            #[doc = concat!("The `", stringify!($val), "` (\"", stringify!($name),"\") opcode.")]
341            pub const $name: u8 = $val;
342        )*
343        impl OpCode {$(
344            #[doc = concat!("The `", stringify!($val), "` (\"", stringify!($name),"\") opcode.")]
345            pub const $name: Self = Self($val);
346        )*}
347
348        /// Maps each opcode to its info.
349        pub static OPCODE_INFO: [Option<OpCodeInfo>; 256] = {
350            let mut map = [None; 256];
351            let mut prev: u8 = 0;
352            $(
353                let val: u8 = $val;
354                assert!(val == 0 || val > prev, "opcodes must be sorted in ascending order");
355                prev = val;
356                let info = OpCodeInfo::new(stringify!($name));
357                $(
358                let info = $modifier(info, $($($modifier_arg),*)?);
359                )*
360                map[$val] = Some(info);
361            )*
362            let _ = prev;
363            map
364        };
365
366
367        /// Maps each name to its opcode.
368        #[cfg(feature = "parse")]
369        pub(crate) static NAME_TO_OPCODE: phf::Map<&'static str, OpCode> = stringify_with_cb! { phf_map_cb; $($name)* };
370    };
371}
372
373/// Callback for creating a [`phf`] map with `stringify_with_cb`.
374#[cfg(feature = "parse")]
375macro_rules! phf_map_cb {
376    ($(#[doc = $s:literal] $id:ident)*) => {
377        phf::phf_map! {
378            $($s => OpCode::$id),*
379        }
380    };
381}
382
383/// Stringifies identifiers with `paste` so that they are available as literals.
384///
385/// This doesn't work with [`stringify!`] because it cannot be expanded inside of another macro.
386#[cfg(feature = "parse")]
387macro_rules! stringify_with_cb {
388    ($callback:ident; $($id:ident)*) => { paste::paste! {
389        $callback! { $(#[doc = "" $id ""] $id)* }
390    }};
391}
392
393// When adding new opcodes:
394// 1. add the opcode to the list below; make sure it's sorted by opcode value
395// 2. implement the opcode in the corresponding module;
396//    the function signature must be the exact same as the others
397opcodes! {
398    0x00 => STOP     => stack_io(0, 0), terminating;
399    0x01 => ADD      => stack_io(2, 1);
400    0x02 => MUL      => stack_io(2, 1);
401    0x03 => SUB      => stack_io(2, 1);
402    0x04 => DIV      => stack_io(2, 1);
403    0x05 => SDIV     => stack_io(2, 1);
404    0x06 => MOD      => stack_io(2, 1);
405    0x07 => SMOD     => stack_io(2, 1);
406    0x08 => ADDMOD   => stack_io(3, 1);
407    0x09 => MULMOD   => stack_io(3, 1);
408    0x0A => EXP      => stack_io(2, 1);
409    0x0B => SIGNEXTEND => stack_io(2, 1);
410    // 0x0C
411    // 0x0D
412    // 0x0E
413    // 0x0F
414    0x10 => LT   => stack_io(2, 1);
415    0x11 => GT   => stack_io(2, 1);
416    0x12 => SLT  => stack_io(2, 1);
417    0x13 => SGT  => stack_io(2, 1);
418    0x14 => EQ   => stack_io(2, 1);
419    0x15 => ISZERO => stack_io(1, 1);
420    0x16 => AND  => stack_io(2, 1);
421    0x17 => OR   => stack_io(2, 1);
422    0x18 => XOR  => stack_io(2, 1);
423    0x19 => NOT  => stack_io(1, 1);
424    0x1A => BYTE => stack_io(2, 1);
425    0x1B => SHL  => stack_io(2, 1);
426    0x1C => SHR  => stack_io(2, 1);
427    0x1D => SAR  => stack_io(2, 1);
428    0x1E => CLZ => stack_io(1, 1);
429    // 0x1F
430    0x20 => KECCAK256 => stack_io(2, 1);
431    // 0x21
432    // 0x22
433    // 0x23
434    // 0x24
435    // 0x25
436    // 0x26
437    // 0x27
438    // 0x28
439    // 0x29
440    // 0x2A
441    // 0x2B
442    // 0x2C
443    // 0x2D
444    // 0x2E
445    // 0x2F
446    0x30 => ADDRESS    => stack_io(0, 1);
447    0x31 => BALANCE    => stack_io(1, 1);
448    0x32 => ORIGIN     => stack_io(0, 1);
449    0x33 => CALLER     => stack_io(0, 1);
450    0x34 => CALLVALUE  => stack_io(0, 1);
451    0x35 => CALLDATALOAD => stack_io(1, 1);
452    0x36 => CALLDATASIZE => stack_io(0, 1);
453    0x37 => CALLDATACOPY => stack_io(3, 0);
454    0x38 => CODESIZE   => stack_io(0, 1);
455    0x39 => CODECOPY   => stack_io(3, 0);
456
457    0x3A => GASPRICE     => stack_io(0, 1);
458    0x3B => EXTCODESIZE  => stack_io(1, 1);
459    0x3C => EXTCODECOPY  => stack_io(4, 0);
460    0x3D => RETURNDATASIZE => stack_io(0, 1);
461    0x3E => RETURNDATACOPY => stack_io(3, 0);
462    0x3F => EXTCODEHASH  => stack_io(1, 1);
463    0x40 => BLOCKHASH    => stack_io(1, 1);
464    0x41 => COINBASE     => stack_io(0, 1);
465    0x42 => TIMESTAMP    => stack_io(0, 1);
466    0x43 => NUMBER       => stack_io(0, 1);
467    0x44 => DIFFICULTY   => stack_io(0, 1);
468    0x45 => GASLIMIT     => stack_io(0, 1);
469    0x46 => CHAINID      => stack_io(0, 1);
470    0x47 => SELFBALANCE  => stack_io(0, 1);
471    0x48 => BASEFEE      => stack_io(0, 1);
472    0x49 => BLOBHASH     => stack_io(1, 1);
473    0x4A => BLOBBASEFEE  => stack_io(0, 1);
474    0x4B => SLOTNUM      => stack_io(0, 1);
475    // 0x4C
476    // 0x4D
477    // 0x4E
478    // 0x4F
479    0x50 => POP      => stack_io(1, 0);
480    0x51 => MLOAD    => stack_io(1, 1);
481    0x52 => MSTORE   => stack_io(2, 0);
482    0x53 => MSTORE8  => stack_io(2, 0);
483    0x54 => SLOAD    => stack_io(1, 1);
484    0x55 => SSTORE   => stack_io(2, 0);
485    0x56 => JUMP     => stack_io(1, 0);
486    0x57 => JUMPI    => stack_io(2, 0);
487    0x58 => PC       => stack_io(0, 1);
488    0x59 => MSIZE    => stack_io(0, 1);
489    0x5A => GAS      => stack_io(0, 1);
490    0x5B => JUMPDEST => stack_io(0, 0);
491    0x5C => TLOAD    => stack_io(1, 1);
492    0x5D => TSTORE   => stack_io(2, 0);
493    0x5E => MCOPY    => stack_io(3, 0);
494
495    0x5F => PUSH0  => stack_io(0, 1);
496    0x60 => PUSH1  => stack_io(0, 1), immediate_size(1);
497    0x61 => PUSH2  => stack_io(0, 1), immediate_size(2);
498    0x62 => PUSH3  => stack_io(0, 1), immediate_size(3);
499    0x63 => PUSH4  => stack_io(0, 1), immediate_size(4);
500    0x64 => PUSH5  => stack_io(0, 1), immediate_size(5);
501    0x65 => PUSH6  => stack_io(0, 1), immediate_size(6);
502    0x66 => PUSH7  => stack_io(0, 1), immediate_size(7);
503    0x67 => PUSH8  => stack_io(0, 1), immediate_size(8);
504    0x68 => PUSH9  => stack_io(0, 1), immediate_size(9);
505    0x69 => PUSH10 => stack_io(0, 1), immediate_size(10);
506    0x6A => PUSH11 => stack_io(0, 1), immediate_size(11);
507    0x6B => PUSH12 => stack_io(0, 1), immediate_size(12);
508    0x6C => PUSH13 => stack_io(0, 1), immediate_size(13);
509    0x6D => PUSH14 => stack_io(0, 1), immediate_size(14);
510    0x6E => PUSH15 => stack_io(0, 1), immediate_size(15);
511    0x6F => PUSH16 => stack_io(0, 1), immediate_size(16);
512    0x70 => PUSH17 => stack_io(0, 1), immediate_size(17);
513    0x71 => PUSH18 => stack_io(0, 1), immediate_size(18);
514    0x72 => PUSH19 => stack_io(0, 1), immediate_size(19);
515    0x73 => PUSH20 => stack_io(0, 1), immediate_size(20);
516    0x74 => PUSH21 => stack_io(0, 1), immediate_size(21);
517    0x75 => PUSH22 => stack_io(0, 1), immediate_size(22);
518    0x76 => PUSH23 => stack_io(0, 1), immediate_size(23);
519    0x77 => PUSH24 => stack_io(0, 1), immediate_size(24);
520    0x78 => PUSH25 => stack_io(0, 1), immediate_size(25);
521    0x79 => PUSH26 => stack_io(0, 1), immediate_size(26);
522    0x7A => PUSH27 => stack_io(0, 1), immediate_size(27);
523    0x7B => PUSH28 => stack_io(0, 1), immediate_size(28);
524    0x7C => PUSH29 => stack_io(0, 1), immediate_size(29);
525    0x7D => PUSH30 => stack_io(0, 1), immediate_size(30);
526    0x7E => PUSH31 => stack_io(0, 1), immediate_size(31);
527    0x7F => PUSH32 => stack_io(0, 1), immediate_size(32);
528
529    0x80 => DUP1  => stack_io(1, 2);
530    0x81 => DUP2  => stack_io(2, 3);
531    0x82 => DUP3  => stack_io(3, 4);
532    0x83 => DUP4  => stack_io(4, 5);
533    0x84 => DUP5  => stack_io(5, 6);
534    0x85 => DUP6  => stack_io(6, 7);
535    0x86 => DUP7  => stack_io(7, 8);
536    0x87 => DUP8  => stack_io(8, 9);
537    0x88 => DUP9  => stack_io(9, 10);
538    0x89 => DUP10 => stack_io(10, 11);
539    0x8A => DUP11 => stack_io(11, 12);
540    0x8B => DUP12 => stack_io(12, 13);
541    0x8C => DUP13 => stack_io(13, 14);
542    0x8D => DUP14 => stack_io(14, 15);
543    0x8E => DUP15 => stack_io(15, 16);
544    0x8F => DUP16 => stack_io(16, 17);
545
546    0x90 => SWAP1  => stack_io(2, 2);
547    0x91 => SWAP2  => stack_io(3, 3);
548    0x92 => SWAP3  => stack_io(4, 4);
549    0x93 => SWAP4  => stack_io(5, 5);
550    0x94 => SWAP5  => stack_io(6, 6);
551    0x95 => SWAP6  => stack_io(7, 7);
552    0x96 => SWAP7  => stack_io(8, 8);
553    0x97 => SWAP8  => stack_io(9, 9);
554    0x98 => SWAP9  => stack_io(10, 10);
555    0x99 => SWAP10 => stack_io(11, 11);
556    0x9A => SWAP11 => stack_io(12, 12);
557    0x9B => SWAP12 => stack_io(13, 13);
558    0x9C => SWAP13 => stack_io(14, 14);
559    0x9D => SWAP14 => stack_io(15, 15);
560    0x9E => SWAP15 => stack_io(16, 16);
561    0x9F => SWAP16 => stack_io(17, 17);
562
563    0xA0 => LOG0 => stack_io(2, 0);
564    0xA1 => LOG1 => stack_io(3, 0);
565    0xA2 => LOG2 => stack_io(4, 0);
566    0xA3 => LOG3 => stack_io(5, 0);
567    0xA4 => LOG4 => stack_io(6, 0);
568    // 0xA5
569    // 0xA6
570    // 0xA7
571    // 0xA8
572    // 0xA9
573    // 0xAA
574    // 0xAB
575    // 0xAC
576    // 0xAD
577    // 0xAE
578    // 0xAF
579    // 0xB0
580    // 0xB1
581    // 0xB2
582    // 0xB3
583    // 0xB4
584    // 0xB5
585    // 0xB6
586    // 0xB7
587    // 0xB8
588    // 0xB9
589    // 0xBA
590    // 0xBB
591    // 0xBC
592    // 0xBD
593    // 0xBE
594    // 0xBF
595    // 0xC0
596    // 0xC1
597    // 0xC2
598    // 0xC3
599    // 0xC4
600    // 0xC5
601    // 0xC6
602    // 0xC7
603    // 0xC8
604    // 0xC9
605    // 0xCA
606    // 0xCB
607    // 0xCC
608    // 0xCD
609    // 0xCE
610    // 0xCF
611    // 0xD0
612    // 0xD1
613    // 0xD2
614    // 0xD3
615    // 0xD4
616    // 0xD5
617    // 0xD6
618    // 0xD7
619    // 0xD8
620    // 0xD9
621    // 0xDA
622    // 0xDB
623    // 0xDC
624    // 0xDD
625    // 0xDE
626    // 0xDF
627    // 0xE0
628    // 0xE1
629    // 0xE2
630    // 0xE3
631    // 0xE4
632    // 0xE5
633    0xE6 => DUPN     => stack_io(0, 1), immediate_size(1);
634    0xE7 => SWAPN    => stack_io(0, 0), immediate_size(1);
635    0xE8 => EXCHANGE => stack_io(0, 0), immediate_size(1);
636    // 0xE9
637    // 0xEA
638    // 0xEB
639    // 0xEC
640    // 0xED
641    // 0xEE
642    // 0xEF
643    0xF0 => CREATE       => stack_io(3, 1);
644    0xF1 => CALL         => stack_io(7, 1);
645    0xF2 => CALLCODE     => stack_io(7, 1);
646    0xF3 => RETURN       => stack_io(2, 0), terminating;
647    0xF4 => DELEGATECALL => stack_io(6, 1);
648    0xF5 => CREATE2      => stack_io(4, 1);
649    // 0xF6
650    // 0xF7
651    // 0xF8
652    // 0xF9
653    0xFA => STATICCALL      => stack_io(6, 1);
654    // 0xFB
655    // 0xFC
656    0xFD => REVERT       => stack_io(2, 0), terminating;
657    0xFE => INVALID      => stack_io(0, 0), terminating;
658    0xFF => SELFDESTRUCT => stack_io(1, 0), terminating;
659}
660
661#[cfg(test)]
662mod tests {
663    use super::*;
664
665    #[test]
666    fn test_opcode() {
667        let opcode = OpCode::new(0x00).unwrap();
668        assert!(!opcode.is_jumpdest());
669        assert!(!opcode.is_jump());
670        assert!(!opcode.is_push());
671        assert_eq!(opcode.as_str(), "STOP");
672        assert_eq!(opcode.get(), 0x00);
673    }
674
675    #[test]
676    fn test_immediate_size() {
677        let mut expected = [0u8; 256];
678
679        for push in PUSH1..=PUSH32 {
680            expected[push as usize] = push - PUSH1 + 1;
681        }
682
683        for stack_op in [DUPN, SWAPN, EXCHANGE] {
684            expected[stack_op as usize] = 1;
685        }
686
687        for (i, opcode) in OPCODE_INFO.iter().enumerate() {
688            if let Some(opcode) = opcode {
689                assert_eq!(
690                    opcode.immediate_size(),
691                    expected[i],
692                    "immediate_size check failed for {opcode:#?}",
693                );
694            }
695        }
696    }
697
698    #[test]
699    fn test_enabled_opcodes() {
700        // List obtained from https://eips.ethereum.org/EIPS/eip-3670
701        let opcodes = [
702            0x10..=0x1d,
703            0x20..=0x20,
704            0x30..=0x3f,
705            0x40..=0x48,
706            0x50..=0x5b,
707            0x54..=0x5f,
708            0x60..=0x6f,
709            0x70..=0x7f,
710            0x80..=0x8f,
711            0x90..=0x9f,
712            0xa0..=0xa4,
713            0xf0..=0xf5,
714            0xfa..=0xfa,
715            0xfd..=0xfd,
716            //0xfe,
717            0xff..=0xff,
718        ];
719        for i in opcodes {
720            for opcode in i {
721                OpCode::new(opcode).expect("Opcode should be valid and enabled");
722            }
723        }
724    }
725
726    #[test]
727    fn count_opcodes() {
728        let mut opcode_num = 0;
729        for _ in OPCODE_INFO.into_iter().flatten() {
730            opcode_num += 1;
731        }
732        assert_eq!(opcode_num, 154);
733    }
734
735    #[test]
736    fn test_terminating_opcodes() {
737        let terminating = [REVERT, RETURN, INVALID, SELFDESTRUCT, STOP];
738        let mut opcodes = [false; 256];
739        for terminating in terminating.iter() {
740            opcodes[*terminating as usize] = true;
741        }
742
743        for (i, opcode) in OPCODE_INFO.into_iter().enumerate() {
744            assert_eq!(
745                opcode.map(|opcode| opcode.terminating).unwrap_or_default(),
746                opcodes[i],
747                "Opcode {opcode:?} terminating check failed."
748            );
749        }
750    }
751
752    #[test]
753    #[cfg(feature = "parse")]
754    fn test_parsing() {
755        for i in 0..=u8::MAX {
756            if let Some(op) = OpCode::new(i) {
757                assert_eq!(OpCode::parse(op.as_str()), Some(op));
758            }
759        }
760    }
761
762    #[test]
763    fn test_new_unchecked_invalid() {
764        let op = OpCode::new_or_unknown(0x0C);
765        assert_eq!(op.info().name(), "UNKNOWN");
766    }
767
768    #[test]
769    fn test_op_code_valid() {
770        let op1 = OpCode::new(ADD).unwrap();
771        let op2 = OpCode::new(MUL).unwrap();
772        assert!(op1.is_valid());
773        assert!(op2.is_valid());
774
775        let op3 = OpCode::new_or_unknown(0x0C);
776        assert!(!op3.is_valid());
777    }
778
779    #[test]
780    fn test_modifies_memory() {
781        assert!(OpCode::new(MLOAD).unwrap().modifies_memory());
782        assert!(OpCode::new(MSTORE).unwrap().modifies_memory());
783        assert!(OpCode::new(KECCAK256).unwrap().modifies_memory());
784        assert!(!OpCode::new(ADD).unwrap().modifies_memory());
785        assert!(OpCode::new(LOG0).unwrap().modifies_memory());
786        assert!(OpCode::new(LOG1).unwrap().modifies_memory());
787        assert!(OpCode::new(LOG2).unwrap().modifies_memory());
788        assert!(OpCode::new(LOG3).unwrap().modifies_memory());
789        assert!(OpCode::new(LOG4).unwrap().modifies_memory());
790        assert!(OpCode::new(RETURN).unwrap().modifies_memory());
791        assert!(OpCode::new(REVERT).unwrap().modifies_memory());
792        assert!(OpCode::new(CREATE).unwrap().modifies_memory());
793        assert!(OpCode::new(CREATE2).unwrap().modifies_memory());
794    }
795}