1#[cfg(feature = "parse")]
4pub mod parse;
5
6use core::{fmt, ptr::NonNull};
7
8#[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 {
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 #[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 #[inline]
42 pub const fn new_or_unknown(opcode: u8) -> Self {
43 Self(opcode)
44 }
45
46 #[inline]
48 pub const fn is_jumpdest(&self) -> bool {
49 Self::is_jumpdest_by_op(self.0)
50 }
51
52 #[inline]
54 pub const fn is_jumpdest_by_op(opcode: u8) -> bool {
55 opcode == JUMPDEST
56 }
57
58 #[inline]
60 pub const fn is_jump(self) -> bool {
61 Self::is_jump_by_op(self.0)
62 }
63
64 #[inline]
66 pub const fn is_jump_by_op(opcode: u8) -> bool {
67 opcode == JUMP
68 }
69
70 #[inline]
72 pub const fn is_push(self) -> bool {
73 Self::is_push_by_op(self.0)
74 }
75
76 #[inline]
78 pub const fn is_push_by_op(opcode: u8) -> bool {
79 opcode >= PUSH1 && opcode <= PUSH32
80 }
81
82 #[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 #[doc(alias = "name")]
97 #[inline]
98 pub const fn as_str(self) -> &'static str {
99 self.info().name()
100 }
101
102 #[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 #[inline]
114 pub const fn inputs(&self) -> u8 {
115 self.info().inputs()
116 }
117
118 #[inline]
120 pub const fn outputs(&self) -> u8 {
121 self.info().outputs()
122 }
123
124 #[inline]
126 pub const fn io_diff(&self) -> i16 {
127 self.info().io_diff()
128 }
129
130 #[inline]
133 pub const fn info_by_op(opcode: u8) -> Option<OpCodeInfo> {
134 OPCODE_INFO[opcode as usize]
135 }
136
137 #[inline]
139 pub const fn as_usize(&self) -> usize {
140 self.0 as usize
141 }
142
143 #[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 pub const fn input_output(&self) -> (u8, u8) {
157 let info = self.info();
158 (info.inputs, info.outputs)
159 }
160
161 #[inline]
163 pub const fn get(self) -> u8 {
164 self.0
165 }
166
167 #[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 #[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#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
216pub struct OpCodeInfo {
217 name_ptr: NonNull<u8>,
222 name_len: u8,
223 inputs: u8,
225 outputs: u8,
227 immediate_size: u8,
229 terminating: bool,
231}
232
233unsafe 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 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 #[inline]
269 pub const fn name(&self) -> &'static str {
270 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 #[inline]
279 pub const fn io_diff(&self) -> i16 {
280 self.outputs as i16 - self.inputs as i16
281 }
282
283 #[inline]
285 pub const fn inputs(&self) -> u8 {
286 self.inputs
287 }
288
289 #[inline]
291 pub const fn outputs(&self) -> u8 {
292 self.outputs
293 }
294
295 #[inline]
297 pub const fn is_terminating(&self) -> bool {
298 self.terminating
299 }
300
301 #[inline]
303 pub const fn immediate_size(&self) -> u8 {
304 self.immediate_size
305 }
306}
307
308#[inline]
310pub const fn immediate_size(mut op: OpCodeInfo, n: u8) -> OpCodeInfo {
311 op.immediate_size = n;
312 op
313}
314
315#[inline]
317pub const fn terminating(mut op: OpCodeInfo) -> OpCodeInfo {
318 op.terminating = true;
319 op
320}
321
322#[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
330pub const NOP: u8 = JUMPDEST;
332
333macro_rules! opcodes {
337 ($($val:literal => $name:ident => $($modifier:ident $(( $($modifier_arg:expr),* ))?),*);* $(;)?) => {
338 $(
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 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 #[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#[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#[cfg(feature = "parse")]
387macro_rules! stringify_with_cb {
388 ($callback:ident; $($id:ident)*) => { paste::paste! {
389 $callback! { $(#[doc = "" $id ""] $id)* }
390 }};
391}
392
393opcodes! {
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 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 0x20 => KECCAK256 => stack_io(2, 1);
431 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 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 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 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 0xFA => STATICCALL => stack_io(6, 1);
654 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 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 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}