revm_bytecode/
opcode.rs

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