1pub mod eof_printer;
4
5mod tables;
6pub use tables::{
7 make_boxed_instruction_table, make_instruction_table, update_boxed_instruction,
8 BoxedInstruction, BoxedInstructionTable, DynInstruction, Instruction, InstructionTable,
9 InstructionTables,
10};
11
12use crate::{instructions::*, primitives::Spec, Host};
13use core::{fmt, ptr::NonNull};
14
15#[derive(Debug, PartialEq, Eq)]
17#[cfg(feature = "parse")]
18pub struct OpCodeError(());
19
20#[cfg(feature = "parse")]
21impl fmt::Display for OpCodeError {
22 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
23 f.write_str("invalid opcode")
24 }
25}
26
27#[cfg(all(feature = "std", feature = "parse"))]
28impl std::error::Error for OpCodeError {}
29
30#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
35#[repr(transparent)]
36pub struct OpCode(u8);
37
38impl fmt::Display for OpCode {
39 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40 let n = self.get();
41 if let Some(val) = OPCODE_INFO_JUMPTABLE[n as usize] {
42 f.write_str(val.name())
43 } else {
44 write!(f, "UNKNOWN(0x{n:02X})")
45 }
46 }
47}
48
49#[cfg(feature = "parse")]
50impl core::str::FromStr for OpCode {
51 type Err = OpCodeError;
52
53 #[inline]
54 fn from_str(s: &str) -> Result<Self, Self::Err> {
55 Self::parse(s).ok_or(OpCodeError(()))
56 }
57}
58
59impl OpCode {
60 #[inline]
62 pub const fn new(opcode: u8) -> Option<Self> {
63 match OPCODE_INFO_JUMPTABLE[opcode as usize] {
64 Some(_) => Some(Self(opcode)),
65 None => None,
66 }
67 }
68
69 #[inline]
71 #[cfg(feature = "parse")]
72 pub fn parse(s: &str) -> Option<Self> {
73 NAME_TO_OPCODE.get(s).copied()
74 }
75
76 #[inline]
78 pub const fn is_jumpdest(&self) -> bool {
79 self.0 == JUMPDEST
80 }
81
82 #[inline]
84 pub const fn is_jumpdest_by_op(opcode: u8) -> bool {
85 if let Some(opcode) = Self::new(opcode) {
86 opcode.is_jumpdest()
87 } else {
88 false
89 }
90 }
91
92 #[inline]
94 pub const fn is_jump(self) -> bool {
95 self.0 == JUMP
96 }
97
98 #[inline]
100 pub const fn is_jump_by_op(opcode: u8) -> bool {
101 if let Some(opcode) = Self::new(opcode) {
102 opcode.is_jump()
103 } else {
104 false
105 }
106 }
107
108 #[inline]
110 pub const fn is_push(self) -> bool {
111 self.0 >= PUSH1 && self.0 <= PUSH32
112 }
113
114 #[inline]
116 pub fn is_push_by_op(opcode: u8) -> bool {
117 if let Some(opcode) = Self::new(opcode) {
118 opcode.is_push()
119 } else {
120 false
121 }
122 }
123
124 #[inline]
131 pub unsafe fn new_unchecked(opcode: u8) -> Self {
132 Self(opcode)
133 }
134
135 #[doc(alias = "name")]
137 #[inline]
138 pub const fn as_str(self) -> &'static str {
139 self.info().name()
140 }
141
142 #[inline]
144 pub const fn name_by_op(opcode: u8) -> &'static str {
145 if let Some(opcode) = Self::new(opcode) {
146 opcode.as_str()
147 } else {
148 "Unknown"
149 }
150 }
151
152 #[inline]
154 pub const fn inputs(&self) -> u8 {
155 self.info().inputs()
156 }
157
158 #[inline]
160 pub const fn outputs(&self) -> u8 {
161 self.info().outputs()
162 }
163
164 #[inline]
166 pub const fn io_diff(&self) -> i16 {
167 self.info().io_diff()
168 }
169
170 #[inline]
172 pub const fn info_by_op(opcode: u8) -> Option<OpCodeInfo> {
173 if let Some(opcode) = Self::new(opcode) {
174 Some(opcode.info())
175 } else {
176 None
177 }
178 }
179
180 #[inline]
182 pub const fn info(&self) -> OpCodeInfo {
183 if let Some(t) = OPCODE_INFO_JUMPTABLE[self.0 as usize] {
184 t
185 } else {
186 panic!("opcode not found")
187 }
188 }
189
190 pub const fn input_output(&self) -> (u8, u8) {
194 let info = self.info();
195 (info.inputs, info.outputs)
196 }
197
198 #[inline]
200 pub const fn get(self) -> u8 {
201 self.0
202 }
203
204 #[inline]
208 pub const fn modifies_memory(&self) -> bool {
209 matches!(
210 *self,
211 OpCode::EXTCODECOPY
212 | OpCode::MLOAD
213 | OpCode::MSTORE
214 | OpCode::MSTORE8
215 | OpCode::MCOPY
216 | OpCode::CODECOPY
217 | OpCode::CALLDATACOPY
218 | OpCode::RETURNDATACOPY
219 | OpCode::CALL
220 | OpCode::CALLCODE
221 | OpCode::DELEGATECALL
222 | OpCode::STATICCALL
223 | OpCode::DATACOPY
224 | OpCode::EOFCREATE
225 | OpCode::RETURNCONTRACT
226 | OpCode::EXTCALL
227 | OpCode::EXTDELEGATECALL
228 | OpCode::EXTSTATICCALL
229 )
230 }
231}
232
233#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
235pub struct OpCodeInfo {
236 name_ptr: NonNull<u8>,
239 name_len: u8,
240 inputs: u8,
242 outputs: u8,
244 immediate_size: u8,
249 not_eof: bool,
251 terminating: bool,
253}
254
255impl fmt::Debug for OpCodeInfo {
256 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
257 f.debug_struct("OpCodeInfo")
258 .field("name", &self.name())
259 .field("inputs", &self.inputs())
260 .field("outputs", &self.outputs())
261 .field("not_eof", &self.is_disabled_in_eof())
262 .field("terminating", &self.is_terminating())
263 .field("immediate_size", &self.immediate_size())
264 .finish()
265 }
266}
267
268impl OpCodeInfo {
269 pub const fn new(name: &'static str) -> Self {
271 assert!(name.len() < 256, "opcode name is too long");
272 Self {
273 name_ptr: unsafe { NonNull::new_unchecked(name.as_ptr().cast_mut()) },
274 name_len: name.len() as u8,
275 inputs: 0,
276 outputs: 0,
277 not_eof: false,
278 terminating: false,
279 immediate_size: 0,
280 }
281 }
282
283 #[inline]
285 pub const fn name(&self) -> &'static str {
286 unsafe {
288 let slice = core::slice::from_raw_parts(self.name_ptr.as_ptr(), self.name_len as usize);
290 core::str::from_utf8_unchecked(slice)
291 }
292 }
293
294 #[inline]
296 pub const fn io_diff(&self) -> i16 {
297 self.outputs as i16 - self.inputs as i16
298 }
299
300 #[inline]
302 pub const fn inputs(&self) -> u8 {
303 self.inputs
304 }
305
306 #[inline]
308 pub const fn outputs(&self) -> u8 {
309 self.outputs
310 }
311
312 #[inline]
314 pub const fn is_disabled_in_eof(&self) -> bool {
315 self.not_eof
316 }
317
318 #[inline]
320 pub const fn is_terminating(&self) -> bool {
321 self.terminating
322 }
323
324 #[inline]
326 pub const fn immediate_size(&self) -> u8 {
327 self.immediate_size
328 }
329}
330
331#[inline]
333pub const fn not_eof(mut op: OpCodeInfo) -> OpCodeInfo {
334 op.not_eof = true;
335 op
336}
337
338#[inline]
343pub const fn immediate_size(mut op: OpCodeInfo, n: u8) -> OpCodeInfo {
344 op.immediate_size = n;
345 op
346}
347
348#[inline]
350pub const fn terminating(mut op: OpCodeInfo) -> OpCodeInfo {
351 op.terminating = true;
352 op
353}
354
355#[inline]
357pub const fn stack_io(mut op: OpCodeInfo, inputs: u8, outputs: u8) -> OpCodeInfo {
358 op.inputs = inputs;
359 op.outputs = outputs;
360 op
361}
362
363pub const NOP: u8 = JUMPDEST;
365
366#[cfg(feature = "parse")]
368macro_rules! phf_map_cb {
369 ($(#[doc = $s:literal] $id:ident)*) => {
370 phf::phf_map! {
371 $($s => OpCode::$id),*
372 }
373 };
374}
375
376#[cfg(feature = "parse")]
379macro_rules! stringify_with_cb {
380 ($callback:ident; $($id:ident)*) => { paste::paste! {
381 $callback! { $(#[doc = "" $id ""] $id)* }
382 }};
383}
384
385macro_rules! opcodes {
386 ($($val:literal => $name:ident => $f:expr => $($modifier:ident $(( $($modifier_arg:expr),* ))?),*);* $(;)?) => {
387 $(
389 #[doc = concat!("The `", stringify!($val), "` (\"", stringify!($name),"\") opcode.")]
390 pub const $name: u8 = $val;
391 )*
392 impl OpCode {$(
393 #[doc = concat!("The `", stringify!($val), "` (\"", stringify!($name),"\") opcode.")]
394 pub const $name: Self = Self($val);
395 )*}
396
397 pub const OPCODE_INFO_JUMPTABLE: [Option<OpCodeInfo>; 256] = {
399 let mut map = [None; 256];
400 let mut prev: u8 = 0;
401 $(
402 let val: u8 = $val;
403 assert!(val == 0 || val > prev, "opcodes must be sorted in ascending order");
404 prev = val;
405 let info = OpCodeInfo::new(stringify!($name));
406 $(
407 let info = $modifier(info, $($($modifier_arg),*)?);
408 )*
409 map[$val] = Some(info);
410 )*
411 let _ = prev;
412 map
413 };
414
415 #[cfg(feature = "parse")]
417 static NAME_TO_OPCODE: phf::Map<&'static str, OpCode> = stringify_with_cb! { phf_map_cb; $($name)* };
418
419 pub const fn instruction<H: Host + ?Sized, SPEC: Spec>(opcode: u8) -> Instruction<H> {
421 match opcode {
422 $($name => $f,)*
423 _ => control::unknown,
424 }
425 }
426 };
427}
428
429opcodes! {
434 0x00 => STOP => control::stop => stack_io(0, 0), terminating;
435
436 0x01 => ADD => arithmetic::add => stack_io(2, 1);
437 0x02 => MUL => arithmetic::mul => stack_io(2, 1);
438 0x03 => SUB => arithmetic::sub => stack_io(2, 1);
439 0x04 => DIV => arithmetic::div => stack_io(2, 1);
440 0x05 => SDIV => arithmetic::sdiv => stack_io(2, 1);
441 0x06 => MOD => arithmetic::rem => stack_io(2, 1);
442 0x07 => SMOD => arithmetic::smod => stack_io(2, 1);
443 0x08 => ADDMOD => arithmetic::addmod => stack_io(3, 1);
444 0x09 => MULMOD => arithmetic::mulmod => stack_io(3, 1);
445 0x0A => EXP => arithmetic::exp::<H, SPEC> => stack_io(2, 1);
446 0x0B => SIGNEXTEND => arithmetic::signextend => stack_io(2, 1);
447 0x10 => LT => bitwise::lt => stack_io(2, 1);
452 0x11 => GT => bitwise::gt => stack_io(2, 1);
453 0x12 => SLT => bitwise::slt => stack_io(2, 1);
454 0x13 => SGT => bitwise::sgt => stack_io(2, 1);
455 0x14 => EQ => bitwise::eq => stack_io(2, 1);
456 0x15 => ISZERO => bitwise::iszero => stack_io(1, 1);
457 0x16 => AND => bitwise::bitand => stack_io(2, 1);
458 0x17 => OR => bitwise::bitor => stack_io(2, 1);
459 0x18 => XOR => bitwise::bitxor => stack_io(2, 1);
460 0x19 => NOT => bitwise::not => stack_io(1, 1);
461 0x1A => BYTE => bitwise::byte => stack_io(2, 1);
462 0x1B => SHL => bitwise::shl::<H, SPEC> => stack_io(2, 1);
463 0x1C => SHR => bitwise::shr::<H, SPEC> => stack_io(2, 1);
464 0x1D => SAR => bitwise::sar::<H, SPEC> => stack_io(2, 1);
465 0x20 => KECCAK256 => system::keccak256 => stack_io(2, 1);
468 0x30 => ADDRESS => system::address => stack_io(0, 1);
484 0x31 => BALANCE => host::balance::<H, SPEC> => stack_io(1, 1);
485 0x32 => ORIGIN => host_env::origin => stack_io(0, 1);
486 0x33 => CALLER => system::caller => stack_io(0, 1);
487 0x34 => CALLVALUE => system::callvalue => stack_io(0, 1);
488 0x35 => CALLDATALOAD => system::calldataload => stack_io(1, 1);
489 0x36 => CALLDATASIZE => system::calldatasize => stack_io(0, 1);
490 0x37 => CALLDATACOPY => system::calldatacopy => stack_io(3, 0);
491 0x38 => CODESIZE => system::codesize => stack_io(0, 1), not_eof;
492 0x39 => CODECOPY => system::codecopy => stack_io(3, 0), not_eof;
493
494 0x3A => GASPRICE => host_env::gasprice => stack_io(0, 1);
495 0x3B => EXTCODESIZE => host::extcodesize::<H, SPEC> => stack_io(1, 1), not_eof;
496 0x3C => EXTCODECOPY => host::extcodecopy::<H, SPEC> => stack_io(4, 0), not_eof;
497 0x3D => RETURNDATASIZE => system::returndatasize::<H, SPEC> => stack_io(0, 1);
498 0x3E => RETURNDATACOPY => system::returndatacopy::<H, SPEC> => stack_io(3, 0);
499 0x3F => EXTCODEHASH => host::extcodehash::<H, SPEC> => stack_io(1, 1), not_eof;
500 0x40 => BLOCKHASH => host::blockhash::<H, SPEC> => stack_io(1, 1);
501 0x41 => COINBASE => host_env::coinbase => stack_io(0, 1);
502 0x42 => TIMESTAMP => host_env::timestamp => stack_io(0, 1);
503 0x43 => NUMBER => host_env::block_number => stack_io(0, 1);
504 0x44 => DIFFICULTY => host_env::difficulty::<H, SPEC> => stack_io(0, 1);
505 0x45 => GASLIMIT => host_env::gaslimit => stack_io(0, 1);
506 0x46 => CHAINID => host_env::chainid::<H, SPEC> => stack_io(0, 1);
507 0x47 => SELFBALANCE => host::selfbalance::<H, SPEC> => stack_io(0, 1);
508 0x48 => BASEFEE => host_env::basefee::<H, SPEC> => stack_io(0, 1);
509 0x49 => BLOBHASH => host_env::blob_hash::<H, SPEC> => stack_io(1, 1);
510 0x4A => BLOBBASEFEE => host_env::blob_basefee::<H, SPEC> => stack_io(0, 1);
511 0x50 => POP => stack::pop => stack_io(1, 0);
517 0x51 => MLOAD => memory::mload => stack_io(1, 1);
518 0x52 => MSTORE => memory::mstore => stack_io(2, 0);
519 0x53 => MSTORE8 => memory::mstore8 => stack_io(2, 0);
520 0x54 => SLOAD => host::sload::<H, SPEC> => stack_io(1, 1);
521 0x55 => SSTORE => host::sstore::<H, SPEC> => stack_io(2, 0);
522 0x56 => JUMP => control::jump => stack_io(1, 0), not_eof;
523 0x57 => JUMPI => control::jumpi => stack_io(2, 0), not_eof;
524 0x58 => PC => control::pc => stack_io(0, 1), not_eof;
525 0x59 => MSIZE => memory::msize => stack_io(0, 1);
526 0x5A => GAS => system::gas => stack_io(0, 1), not_eof;
527 0x5B => JUMPDEST => control::jumpdest_or_nop => stack_io(0, 0);
528 0x5C => TLOAD => host::tload::<H, SPEC> => stack_io(1, 1);
529 0x5D => TSTORE => host::tstore::<H, SPEC> => stack_io(2, 0);
530 0x5E => MCOPY => memory::mcopy::<H, SPEC> => stack_io(3, 0);
531
532 0x5F => PUSH0 => stack::push0::<H, SPEC> => stack_io(0, 1);
533 0x60 => PUSH1 => stack::push::<1, H> => stack_io(0, 1), immediate_size(1);
534 0x61 => PUSH2 => stack::push::<2, H> => stack_io(0, 1), immediate_size(2);
535 0x62 => PUSH3 => stack::push::<3, H> => stack_io(0, 1), immediate_size(3);
536 0x63 => PUSH4 => stack::push::<4, H> => stack_io(0, 1), immediate_size(4);
537 0x64 => PUSH5 => stack::push::<5, H> => stack_io(0, 1), immediate_size(5);
538 0x65 => PUSH6 => stack::push::<6, H> => stack_io(0, 1), immediate_size(6);
539 0x66 => PUSH7 => stack::push::<7, H> => stack_io(0, 1), immediate_size(7);
540 0x67 => PUSH8 => stack::push::<8, H> => stack_io(0, 1), immediate_size(8);
541 0x68 => PUSH9 => stack::push::<9, H> => stack_io(0, 1), immediate_size(9);
542 0x69 => PUSH10 => stack::push::<10, H> => stack_io(0, 1), immediate_size(10);
543 0x6A => PUSH11 => stack::push::<11, H> => stack_io(0, 1), immediate_size(11);
544 0x6B => PUSH12 => stack::push::<12, H> => stack_io(0, 1), immediate_size(12);
545 0x6C => PUSH13 => stack::push::<13, H> => stack_io(0, 1), immediate_size(13);
546 0x6D => PUSH14 => stack::push::<14, H> => stack_io(0, 1), immediate_size(14);
547 0x6E => PUSH15 => stack::push::<15, H> => stack_io(0, 1), immediate_size(15);
548 0x6F => PUSH16 => stack::push::<16, H> => stack_io(0, 1), immediate_size(16);
549 0x70 => PUSH17 => stack::push::<17, H> => stack_io(0, 1), immediate_size(17);
550 0x71 => PUSH18 => stack::push::<18, H> => stack_io(0, 1), immediate_size(18);
551 0x72 => PUSH19 => stack::push::<19, H> => stack_io(0, 1), immediate_size(19);
552 0x73 => PUSH20 => stack::push::<20, H> => stack_io(0, 1), immediate_size(20);
553 0x74 => PUSH21 => stack::push::<21, H> => stack_io(0, 1), immediate_size(21);
554 0x75 => PUSH22 => stack::push::<22, H> => stack_io(0, 1), immediate_size(22);
555 0x76 => PUSH23 => stack::push::<23, H> => stack_io(0, 1), immediate_size(23);
556 0x77 => PUSH24 => stack::push::<24, H> => stack_io(0, 1), immediate_size(24);
557 0x78 => PUSH25 => stack::push::<25, H> => stack_io(0, 1), immediate_size(25);
558 0x79 => PUSH26 => stack::push::<26, H> => stack_io(0, 1), immediate_size(26);
559 0x7A => PUSH27 => stack::push::<27, H> => stack_io(0, 1), immediate_size(27);
560 0x7B => PUSH28 => stack::push::<28, H> => stack_io(0, 1), immediate_size(28);
561 0x7C => PUSH29 => stack::push::<29, H> => stack_io(0, 1), immediate_size(29);
562 0x7D => PUSH30 => stack::push::<30, H> => stack_io(0, 1), immediate_size(30);
563 0x7E => PUSH31 => stack::push::<31, H> => stack_io(0, 1), immediate_size(31);
564 0x7F => PUSH32 => stack::push::<32, H> => stack_io(0, 1), immediate_size(32);
565
566 0x80 => DUP1 => stack::dup::<1, H> => stack_io(1, 2);
567 0x81 => DUP2 => stack::dup::<2, H> => stack_io(2, 3);
568 0x82 => DUP3 => stack::dup::<3, H> => stack_io(3, 4);
569 0x83 => DUP4 => stack::dup::<4, H> => stack_io(4, 5);
570 0x84 => DUP5 => stack::dup::<5, H> => stack_io(5, 6);
571 0x85 => DUP6 => stack::dup::<6, H> => stack_io(6, 7);
572 0x86 => DUP7 => stack::dup::<7, H> => stack_io(7, 8);
573 0x87 => DUP8 => stack::dup::<8, H> => stack_io(8, 9);
574 0x88 => DUP9 => stack::dup::<9, H> => stack_io(9, 10);
575 0x89 => DUP10 => stack::dup::<10, H> => stack_io(10, 11);
576 0x8A => DUP11 => stack::dup::<11, H> => stack_io(11, 12);
577 0x8B => DUP12 => stack::dup::<12, H> => stack_io(12, 13);
578 0x8C => DUP13 => stack::dup::<13, H> => stack_io(13, 14);
579 0x8D => DUP14 => stack::dup::<14, H> => stack_io(14, 15);
580 0x8E => DUP15 => stack::dup::<15, H> => stack_io(15, 16);
581 0x8F => DUP16 => stack::dup::<16, H> => stack_io(16, 17);
582
583 0x90 => SWAP1 => stack::swap::<1, H> => stack_io(2, 2);
584 0x91 => SWAP2 => stack::swap::<2, H> => stack_io(3, 3);
585 0x92 => SWAP3 => stack::swap::<3, H> => stack_io(4, 4);
586 0x93 => SWAP4 => stack::swap::<4, H> => stack_io(5, 5);
587 0x94 => SWAP5 => stack::swap::<5, H> => stack_io(6, 6);
588 0x95 => SWAP6 => stack::swap::<6, H> => stack_io(7, 7);
589 0x96 => SWAP7 => stack::swap::<7, H> => stack_io(8, 8);
590 0x97 => SWAP8 => stack::swap::<8, H> => stack_io(9, 9);
591 0x98 => SWAP9 => stack::swap::<9, H> => stack_io(10, 10);
592 0x99 => SWAP10 => stack::swap::<10, H> => stack_io(11, 11);
593 0x9A => SWAP11 => stack::swap::<11, H> => stack_io(12, 12);
594 0x9B => SWAP12 => stack::swap::<12, H> => stack_io(13, 13);
595 0x9C => SWAP13 => stack::swap::<13, H> => stack_io(14, 14);
596 0x9D => SWAP14 => stack::swap::<14, H> => stack_io(15, 15);
597 0x9E => SWAP15 => stack::swap::<15, H> => stack_io(16, 16);
598 0x9F => SWAP16 => stack::swap::<16, H> => stack_io(17, 17);
599
600 0xA0 => LOG0 => host::log::<0, H> => stack_io(2, 0);
601 0xA1 => LOG1 => host::log::<1, H> => stack_io(3, 0);
602 0xA2 => LOG2 => host::log::<2, H> => stack_io(4, 0);
603 0xA3 => LOG3 => host::log::<3, H> => stack_io(5, 0);
604 0xA4 => LOG4 => host::log::<4, H> => stack_io(6, 0);
605 0xD0 => DATALOAD => data::data_load => stack_io(1, 1);
649 0xD1 => DATALOADN => data::data_loadn => stack_io(0, 1), immediate_size(2);
650 0xD2 => DATASIZE => data::data_size => stack_io(0, 1);
651 0xD3 => DATACOPY => data::data_copy => stack_io(3, 0);
652 0xE0 => RJUMP => control::rjump => stack_io(0, 0), immediate_size(2), terminating;
665 0xE1 => RJUMPI => control::rjumpi => stack_io(1, 0), immediate_size(2);
666 0xE2 => RJUMPV => control::rjumpv => stack_io(1, 0), immediate_size(1);
667 0xE3 => CALLF => control::callf => stack_io(0, 0), immediate_size(2);
668 0xE4 => RETF => control::retf => stack_io(0, 0), terminating;
669 0xE5 => JUMPF => control::jumpf => stack_io(0, 0), immediate_size(2), terminating;
670 0xE6 => DUPN => stack::dupn => stack_io(0, 1), immediate_size(1);
671 0xE7 => SWAPN => stack::swapn => stack_io(0, 0), immediate_size(1);
672 0xE8 => EXCHANGE => stack::exchange => stack_io(0, 0), immediate_size(1);
673 0xEC => EOFCREATE => contract::eofcreate => stack_io(4, 1), immediate_size(1);
677 0xEE => RETURNCONTRACT => contract::return_contract => stack_io(2, 0), immediate_size(1), terminating;
679 0xF0 => CREATE => contract::create::<false, H, SPEC> => stack_io(3, 1), not_eof;
681 0xF1 => CALL => contract::call::<H, SPEC> => stack_io(7, 1), not_eof;
682 0xF2 => CALLCODE => contract::call_code::<H, SPEC> => stack_io(7, 1), not_eof;
683 0xF3 => RETURN => control::ret => stack_io(2, 0), terminating;
684 0xF4 => DELEGATECALL => contract::delegate_call::<H, SPEC> => stack_io(6, 1), not_eof;
685 0xF5 => CREATE2 => contract::create::<true, H, SPEC> => stack_io(4, 1), not_eof;
686 0xF7 => RETURNDATALOAD => system::returndataload => stack_io(1, 1);
688 0xF8 => EXTCALL => contract::extcall::<H, SPEC> => stack_io(4, 1);
689 0xF9 => EXTDELEGATECALL => contract::extdelegatecall::<H, SPEC> => stack_io(3, 1);
690 0xFA => STATICCALL => contract::static_call::<H, SPEC> => stack_io(6, 1), not_eof;
691 0xFB => EXTSTATICCALL => contract::extstaticcall => stack_io(3, 1);
692 0xFD => REVERT => control::revert::<H, SPEC> => stack_io(2, 0), terminating;
694 0xFE => INVALID => control::invalid => stack_io(0, 0), terminating;
695 0xFF => SELFDESTRUCT => host::selfdestruct::<H, SPEC> => stack_io(1, 0), not_eof, terminating;
696}
697
698#[cfg(test)]
699mod tests {
700 use super::*;
701
702 #[test]
703 fn test_opcode() {
704 let opcode = OpCode::new(0x00).unwrap();
705 assert!(!opcode.is_jumpdest());
706 assert!(!opcode.is_jump());
707 assert!(!opcode.is_push());
708 assert_eq!(opcode.as_str(), "STOP");
709 assert_eq!(opcode.get(), 0x00);
710 }
711
712 #[test]
713 fn test_eof_disable() {
714 const REJECTED_IN_EOF: &[u8] = &[
715 0x38, 0x39, 0x3b, 0x3c, 0x3f, 0x5a, 0xf1, 0xf2, 0xf4, 0xfa, 0xff,
716 ];
717
718 for opcode in REJECTED_IN_EOF {
719 let opcode = OpCode::new(*opcode).unwrap();
720 assert!(
721 opcode.info().is_disabled_in_eof(),
722 "not disabled in EOF: {opcode:#?}",
723 );
724 }
725 }
726
727 #[test]
728 fn test_immediate_size() {
729 let mut expected = [0u8; 256];
730 for push in PUSH1..=PUSH32 {
732 expected[push as usize] = push - PUSH1 + 1;
733 }
734 expected[DATALOADN as usize] = 2;
735 expected[RJUMP as usize] = 2;
736 expected[RJUMPI as usize] = 2;
737 expected[RJUMPV as usize] = 1;
738 expected[CALLF as usize] = 2;
739 expected[JUMPF as usize] = 2;
740 expected[DUPN as usize] = 1;
741 expected[SWAPN as usize] = 1;
742 expected[EXCHANGE as usize] = 1;
743 expected[EOFCREATE as usize] = 1;
744 expected[RETURNCONTRACT as usize] = 1;
745
746 for (i, opcode) in OPCODE_INFO_JUMPTABLE.iter().enumerate() {
747 if let Some(opcode) = opcode {
748 assert_eq!(
749 opcode.immediate_size(),
750 expected[i],
751 "immediate_size check failed for {opcode:#?}",
752 );
753 }
754 }
755 }
756
757 #[test]
758 fn test_enabled_opcodes() {
759 let opcodes = [
761 0x10..=0x1d,
762 0x20..=0x20,
763 0x30..=0x3f,
764 0x40..=0x48,
765 0x50..=0x5b,
766 0x54..=0x5f,
767 0x60..=0x6f,
768 0x70..=0x7f,
769 0x80..=0x8f,
770 0x90..=0x9f,
771 0xa0..=0xa4,
772 0xf0..=0xf5,
773 0xfa..=0xfa,
774 0xfd..=0xfd,
775 0xff..=0xff,
777 ];
778 for i in opcodes {
779 for opcode in i {
780 OpCode::new(opcode).expect("Opcode should be valid and enabled");
781 }
782 }
783 }
784
785 #[test]
786 fn count_opcodes() {
787 let mut opcode_num = 0;
788 let mut eof_opcode_num = 0;
789 for opcode in OPCODE_INFO_JUMPTABLE.into_iter().flatten() {
790 opcode_num += 1;
791 if !opcode.is_disabled_in_eof() {
792 eof_opcode_num += 1;
793 }
794 }
795 assert_eq!(opcode_num, 168);
796 assert_eq!(eof_opcode_num, 152);
797 }
798
799 #[test]
800 fn test_terminating_opcodes() {
801 let terminating = [
802 RETF,
803 REVERT,
804 RETURN,
805 INVALID,
806 SELFDESTRUCT,
807 RETURNCONTRACT,
808 STOP,
809 RJUMP,
810 JUMPF,
811 ];
812 let mut opcodes = [false; 256];
813 for terminating in terminating.iter() {
814 opcodes[*terminating as usize] = true;
815 }
816
817 for (i, opcode) in OPCODE_INFO_JUMPTABLE.into_iter().enumerate() {
818 assert_eq!(
819 opcode.map(|opcode| opcode.terminating).unwrap_or_default(),
820 opcodes[i],
821 "Opcode {:?} terminating chack failed.",
822 opcode
823 );
824 }
825 }
826
827 #[test]
828 #[cfg(feature = "parse")]
829 fn test_parsing() {
830 for i in 0..=u8::MAX {
831 if let Some(op) = OpCode::new(i) {
832 assert_eq!(OpCode::parse(op.as_str()), Some(op));
833 }
834 }
835 }
836}