1pub mod eof_printer;
4
5use crate::{instructions::*, primitives::Spec, Host, Interpreter};
6use core::fmt;
7use std::boxed::Box;
8
9pub type Instruction<H> = fn(&mut Interpreter, &mut H);
11
12pub type InstructionTable<H> = [Instruction<H>; 256];
15
16pub type BoxedInstruction<'a, H> = Box<dyn Fn(&mut Interpreter, &mut H) + 'a>;
18
19pub type BoxedInstructionTable<'a, H> = [BoxedInstruction<'a, H>; 256];
21
22pub enum InstructionTables<'a, H> {
29 Plain(InstructionTable<H>),
30 Boxed(BoxedInstructionTable<'a, H>),
31}
32
33impl<H: Host> InstructionTables<'_, H> {
34 #[inline]
36 pub const fn new_plain<SPEC: Spec>() -> Self {
37 Self::Plain(make_instruction_table::<H, SPEC>())
38 }
39}
40
41impl<'a, H: Host + 'a> InstructionTables<'a, H> {
42 #[inline]
47 pub fn insert_boxed(&mut self, opcode: u8, instruction: BoxedInstruction<'a, H>) {
48 self.convert_boxed();
50
51 match self {
53 Self::Plain(_) => {
54 unreachable!("we already converted the table to boxed variant");
55 }
56 Self::Boxed(table) => {
57 table[opcode as usize] = Box::new(instruction);
58 }
59 }
60 }
61
62 #[inline]
64 pub fn insert(&mut self, opcode: u8, instruction: Instruction<H>) {
65 match self {
66 Self::Plain(table) => {
67 table[opcode as usize] = instruction;
68 }
69 Self::Boxed(table) => {
70 table[opcode as usize] = Box::new(instruction);
71 }
72 }
73 }
74
75 #[inline]
78 pub fn convert_boxed(&mut self) {
79 match self {
80 Self::Plain(table) => {
81 *self = Self::Boxed(core::array::from_fn(|i| {
82 let instruction: BoxedInstruction<'a, H> = Box::new(table[i]);
83 instruction
84 }));
85 }
86 Self::Boxed(_) => {}
87 };
88 }
89}
90
91#[inline]
93pub const fn make_instruction_table<H: Host + ?Sized, SPEC: Spec>() -> InstructionTable<H> {
94 struct ConstTable<H: Host + ?Sized, SPEC: Spec> {
97 _host: core::marker::PhantomData<H>,
98 _spec: core::marker::PhantomData<SPEC>,
99 }
100 impl<H: Host + ?Sized, SPEC: Spec> ConstTable<H, SPEC> {
101 const NEW: InstructionTable<H> = {
102 let mut tables: InstructionTable<H> = [control::unknown; 256];
103 let mut i = 0;
104 while i < 256 {
105 tables[i] = instruction::<H, SPEC>(i as u8);
106 i += 1;
107 }
108 tables
109 };
110 }
111 ConstTable::<H, SPEC>::NEW
112}
113
114#[inline]
116pub fn make_boxed_instruction_table<'a, H, SPEC, FN>(
117 table: InstructionTable<H>,
118 mut outer: FN,
119) -> BoxedInstructionTable<'a, H>
120where
121 H: Host,
122 SPEC: Spec + 'a,
123 FN: FnMut(Instruction<H>) -> BoxedInstruction<'a, H>,
124{
125 core::array::from_fn(|i| outer(table[i]))
126}
127
128#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
133#[repr(transparent)]
134pub struct OpCode(u8);
135
136impl fmt::Display for OpCode {
137 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
138 let n = self.get();
139 if let Some(val) = OPCODE_INFO_JUMPTABLE[n as usize] {
140 f.write_str(val.name)
141 } else {
142 write!(f, "UNKNOWN(0x{n:02X})")
143 }
144 }
145}
146
147impl OpCode {
148 #[inline]
150 pub const fn new(opcode: u8) -> Option<Self> {
151 match OPCODE_INFO_JUMPTABLE[opcode as usize] {
152 Some(_) => Some(Self(opcode)),
153 None => None,
154 }
155 }
156
157 #[inline]
159 pub const fn is_jumpdest(&self) -> bool {
160 self.0 == JUMPDEST
161 }
162
163 #[inline]
165 pub const fn is_jumpdest_by_op(opcode: u8) -> bool {
166 if let Some(opcode) = Self::new(opcode) {
167 opcode.is_jumpdest()
168 } else {
169 false
170 }
171 }
172
173 #[inline]
175 pub const fn is_jump(self) -> bool {
176 self.0 == JUMP
177 }
178
179 #[inline]
181 pub const fn is_jump_by_op(opcode: u8) -> bool {
182 if let Some(opcode) = Self::new(opcode) {
183 opcode.is_jump()
184 } else {
185 false
186 }
187 }
188
189 #[inline]
191 pub const fn is_push(self) -> bool {
192 self.0 >= PUSH1 && self.0 <= PUSH32
193 }
194
195 #[inline]
197 pub fn is_push_by_op(opcode: u8) -> bool {
198 if let Some(opcode) = Self::new(opcode) {
199 opcode.is_push()
200 } else {
201 false
202 }
203 }
204
205 #[inline]
212 pub unsafe fn new_unchecked(opcode: u8) -> Self {
213 Self(opcode)
214 }
215
216 #[inline]
218 pub const fn as_str(self) -> &'static str {
219 self.info().name
220 }
221
222 #[inline]
224 pub const fn name_by_op(opcode: u8) -> &'static str {
225 if let Some(opcode) = Self::new(opcode) {
226 opcode.as_str()
227 } else {
228 "Unknown"
229 }
230 }
231
232 pub const fn inputs(&self) -> u8 {
234 self.info().inputs
235 }
236
237 pub const fn outputs(&self) -> u8 {
239 self.info().outputs
240 }
241
242 pub const fn io_diff(&self) -> i16 {
244 self.info().io_diff()
245 }
246
247 pub const fn info_by_op(opcode: u8) -> Option<OpCodeInfo> {
248 if let Some(opcode) = Self::new(opcode) {
249 Some(opcode.info())
250 } else {
251 None
252 }
253 }
254
255 #[inline]
256 pub const fn info(&self) -> OpCodeInfo {
257 if let Some(t) = OPCODE_INFO_JUMPTABLE[self.0 as usize] {
258 t
259 } else {
260 panic!("unreachable, all opcodes are defined")
261 }
262 }
263
264 pub const fn input_output(&self) -> (u8, u8) {
267 let info = self.info();
268 (info.inputs, info.outputs)
269 }
270
271 #[inline]
273 pub const fn get(self) -> u8 {
274 self.0
275 }
276}
277
278#[derive(Clone, Copy, Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
280pub struct OpCodeInfo {
281 pub name: &'static str,
282 pub inputs: u8,
283 pub outputs: u8,
284 pub is_eof: bool,
286 pub is_terminating_opcode: bool,
288 pub immediate_size: u8,
293}
294
295impl OpCodeInfo {
296 pub const fn new(name: &'static str) -> Self {
297 Self {
298 name,
299 inputs: 0,
300 outputs: 0,
301 is_eof: true,
302 is_terminating_opcode: false,
303 immediate_size: 0,
304 }
305 }
306
307 pub const fn io_diff(&self) -> i16 {
308 self.outputs as i16 - self.inputs as i16
309 }
310}
311
312pub const NOP: u8 = JUMPDEST;
313
314macro_rules! opcodes {
315 ($($val:literal => $name:ident => $f:expr => $($modifier:ident $(< $($modifier_num:literal),* >)?),*);* $(;)?) => {
316 $(
318 #[doc = concat!("The `", stringify!($val), "` (\"", stringify!($name),"\") opcode.")]
319 pub const $name: u8 = $val;
320 )*
321 impl OpCode {$(
322 #[doc = concat!("The `", stringify!($val), "` (\"", stringify!($name),"\") opcode.")]
323 pub const $name: Self = Self($val);
324 )*}
325
326 pub const OPCODE_INFO_JUMPTABLE: [Option<OpCodeInfo>; 256] = {
328 let mut map = [None; 256];
329 let mut prev: u8 = 0;
330 $(
331 let val: u8 = $val;
332 assert!(val == 0 || val > prev, "opcodes must be sorted in ascending order");
333 prev = val;
334 let opcode = OpCodeInfo::new(stringify!($name));
335 $( let opcode = $modifier$(::< $( $modifier_num ),+ >)? (opcode);)*
336 map[$val] = Some(opcode);
337 )*
338 let _ = prev;
339 map
340 };
341
342 pub const fn instruction<H: Host + ?Sized, SPEC: Spec>(opcode: u8) -> Instruction<H> {
344 match opcode {
345 $($name => $f,)*
346 _ => control::unknown,
347 }
348 }
349 };
350}
351
352pub const fn not_eof(mut opcode: OpCodeInfo) -> OpCodeInfo {
353 opcode.is_eof = false;
354 opcode
355}
356
357pub const fn imm_size<const N: u8>(mut opcode: OpCodeInfo) -> OpCodeInfo {
359 opcode.immediate_size = N;
360 opcode
361}
362
363pub const fn terminating(mut opcode: OpCodeInfo) -> OpCodeInfo {
364 opcode.is_terminating_opcode = true;
365 opcode
366}
367
368pub const fn stack_io<const I: u8, const O: u8>(mut opcode: OpCodeInfo) -> OpCodeInfo {
369 opcode.inputs = I;
370 opcode.outputs = O;
371 opcode
372}
373
374opcodes! {
380 0x00 => STOP => control::stop => stack_io<0,0>, terminating;
381
382 0x01 => ADD => arithmetic::add => stack_io<2, 1>;
383 0x02 => MUL => arithmetic::mul => stack_io<2, 1>;
384 0x03 => SUB => arithmetic::sub => stack_io<2, 1>;
385 0x04 => DIV => arithmetic::div => stack_io<2, 1>;
386 0x05 => SDIV => arithmetic::sdiv => stack_io<2, 1>;
387 0x06 => MOD => arithmetic::rem => stack_io<2, 1>;
388 0x07 => SMOD => arithmetic::smod => stack_io<2, 1>;
389 0x08 => ADDMOD => arithmetic::addmod => stack_io<3, 1>;
390 0x09 => MULMOD => arithmetic::mulmod => stack_io<3, 1>;
391 0x0A => EXP => arithmetic::exp::<H, SPEC> => stack_io<2, 1>;
392 0x0B => SIGNEXTEND => arithmetic::signextend => stack_io<2, 1>;
393 0x10 => LT => bitwise::lt => stack_io<2, 1>;
398 0x11 => GT => bitwise::gt => stack_io<2, 1>;
399 0x12 => SLT => bitwise::slt => stack_io<2, 1>;
400 0x13 => SGT => bitwise::sgt => stack_io<2, 1>;
401 0x14 => EQ => bitwise::eq => stack_io<2, 1>;
402 0x15 => ISZERO => bitwise::iszero => stack_io<1, 1>;
403 0x16 => AND => bitwise::bitand => stack_io<2, 1>;
404 0x17 => OR => bitwise::bitor => stack_io<2, 1>;
405 0x18 => XOR => bitwise::bitxor => stack_io<2, 1>;
406 0x19 => NOT => bitwise::not => stack_io<1, 1>;
407 0x1A => BYTE => bitwise::byte => stack_io<2, 1>;
408 0x1B => SHL => bitwise::shl::<H, SPEC> => stack_io<2, 1>;
409 0x1C => SHR => bitwise::shr::<H, SPEC> => stack_io<2, 1>;
410 0x1D => SAR => bitwise::sar::<H, SPEC> => stack_io<2, 1>;
411 0x20 => KECCAK256 => system::keccak256 => stack_io<2, 1>;
414 0x30 => ADDRESS => system::address => stack_io<0, 1>;
430 0x31 => BALANCE => host::balance::<H, SPEC> => stack_io<1, 1>;
431 0x32 => ORIGIN => host_env::origin => stack_io<0, 1>;
432 0x33 => CALLER => system::caller => stack_io<0, 1>;
433 0x34 => CALLVALUE => system::callvalue => stack_io<0, 1>;
434 0x35 => CALLDATALOAD => system::calldataload => stack_io<1, 1>;
435 0x36 => CALLDATASIZE => system::calldatasize => stack_io<0, 1>;
436 0x37 => CALLDATACOPY => system::calldatacopy => stack_io<3, 0>;
437 0x38 => CODESIZE => system::codesize => stack_io<0, 1>, not_eof;
438 0x39 => CODECOPY => system::codecopy => stack_io<3, 0>, not_eof;
439
440 0x3A => GASPRICE => host_env::gasprice => stack_io<0, 1>;
441 0x3B => EXTCODESIZE => host::extcodesize::<H, SPEC> => stack_io<1, 1>, not_eof;
442 0x3C => EXTCODECOPY => host::extcodecopy::<H, SPEC> => stack_io<4, 0>, not_eof;
443 0x3D => RETURNDATASIZE => system::returndatasize::<H, SPEC> => stack_io<0, 1>;
444 0x3E => RETURNDATACOPY => system::returndatacopy::<H, SPEC> => stack_io<3, 0>;
445 0x3F => EXTCODEHASH => host::extcodehash::<H, SPEC> => stack_io<1, 1>, not_eof;
446 0x40 => BLOCKHASH => host::blockhash => stack_io<1, 1>;
447 0x41 => COINBASE => host_env::coinbase => stack_io<0, 1>;
448 0x42 => TIMESTAMP => host_env::timestamp => stack_io<0, 1>;
449 0x43 => NUMBER => host_env::block_number => stack_io<0, 1>;
450 0x44 => DIFFICULTY => host_env::difficulty::<H, SPEC> => stack_io<0, 1>;
451 0x45 => GASLIMIT => host_env::gaslimit => stack_io<0, 1>;
452 0x46 => CHAINID => host_env::chainid::<H, SPEC> => stack_io<0, 1>;
453 0x47 => SELFBALANCE => host::selfbalance::<H, SPEC> => stack_io<0, 1>;
454 0x48 => BASEFEE => host_env::basefee::<H, SPEC> => stack_io<0, 1>;
455 0x49 => BLOBHASH => host_env::blob_hash::<H, SPEC> => stack_io<1, 1>;
456 0x4A => BLOBBASEFEE => host_env::blob_basefee::<H, SPEC> => stack_io<0, 1>;
457 0x50 => POP => stack::pop => stack_io<1, 0>;
463 0x51 => MLOAD => memory::mload => stack_io<1, 1>;
464 0x52 => MSTORE => memory::mstore => stack_io<2, 0>;
465 0x53 => MSTORE8 => memory::mstore8 => stack_io<2, 0>;
466 0x54 => SLOAD => host::sload::<H, SPEC> => stack_io<1, 1>;
467 0x55 => SSTORE => host::sstore::<H, SPEC> => stack_io<2, 0>;
468 0x56 => JUMP => control::jump => stack_io<1, 0>, not_eof;
469 0x57 => JUMPI => control::jumpi => stack_io<2, 0>, not_eof;
470 0x58 => PC => control::pc => stack_io<0, 1>, not_eof;
471 0x59 => MSIZE => memory::msize => stack_io<0, 1>;
472 0x5A => GAS => system::gas => stack_io<0, 1>, not_eof;
473 0x5B => JUMPDEST => control::jumpdest_or_nop => stack_io<0, 0>;
474 0x5C => TLOAD => host::tload::<H, SPEC> => stack_io<1, 1>;
475 0x5D => TSTORE => host::tstore::<H, SPEC> => stack_io<2, 0>;
476 0x5E => MCOPY => memory::mcopy::<H, SPEC> => stack_io<3, 0>;
477
478 0x5F => PUSH0 => stack::push0::<H, SPEC> => stack_io<0, 1>;
479 0x60 => PUSH1 => stack::push::<1, H> => stack_io<0, 1>, imm_size<1>;
480 0x61 => PUSH2 => stack::push::<2, H> => stack_io<0, 1>, imm_size<2>;
481 0x62 => PUSH3 => stack::push::<3, H> => stack_io<0, 1>, imm_size<3>;
482 0x63 => PUSH4 => stack::push::<4, H> => stack_io<0, 1>, imm_size<4>;
483 0x64 => PUSH5 => stack::push::<5, H> => stack_io<0, 1>, imm_size<5>;
484 0x65 => PUSH6 => stack::push::<6, H> => stack_io<0, 1>, imm_size<6>;
485 0x66 => PUSH7 => stack::push::<7, H> => stack_io<0, 1>, imm_size<7>;
486 0x67 => PUSH8 => stack::push::<8, H> => stack_io<0, 1>, imm_size<8>;
487 0x68 => PUSH9 => stack::push::<9, H> => stack_io<0, 1>, imm_size<9>;
488 0x69 => PUSH10 => stack::push::<10, H> => stack_io<0, 1>, imm_size<10>;
489 0x6A => PUSH11 => stack::push::<11, H> => stack_io<0, 1>, imm_size<11>;
490 0x6B => PUSH12 => stack::push::<12, H> => stack_io<0, 1>, imm_size<12>;
491 0x6C => PUSH13 => stack::push::<13, H> => stack_io<0, 1>, imm_size<13>;
492 0x6D => PUSH14 => stack::push::<14, H> => stack_io<0, 1>, imm_size<14>;
493 0x6E => PUSH15 => stack::push::<15, H> => stack_io<0, 1>, imm_size<15>;
494 0x6F => PUSH16 => stack::push::<16, H> => stack_io<0, 1>, imm_size<16>;
495 0x70 => PUSH17 => stack::push::<17, H> => stack_io<0, 1>, imm_size<17>;
496 0x71 => PUSH18 => stack::push::<18, H> => stack_io<0, 1>, imm_size<18>;
497 0x72 => PUSH19 => stack::push::<19, H> => stack_io<0, 1>, imm_size<19>;
498 0x73 => PUSH20 => stack::push::<20, H> => stack_io<0, 1>, imm_size<20>;
499 0x74 => PUSH21 => stack::push::<21, H> => stack_io<0, 1>, imm_size<21>;
500 0x75 => PUSH22 => stack::push::<22, H> => stack_io<0, 1>, imm_size<22>;
501 0x76 => PUSH23 => stack::push::<23, H> => stack_io<0, 1>, imm_size<23>;
502 0x77 => PUSH24 => stack::push::<24, H> => stack_io<0, 1>, imm_size<24>;
503 0x78 => PUSH25 => stack::push::<25, H> => stack_io<0, 1>, imm_size<25>;
504 0x79 => PUSH26 => stack::push::<26, H> => stack_io<0, 1>, imm_size<26>;
505 0x7A => PUSH27 => stack::push::<27, H> => stack_io<0, 1>, imm_size<27>;
506 0x7B => PUSH28 => stack::push::<28, H> => stack_io<0, 1>, imm_size<28>;
507 0x7C => PUSH29 => stack::push::<29, H> => stack_io<0, 1>, imm_size<29>;
508 0x7D => PUSH30 => stack::push::<30, H> => stack_io<0, 1>, imm_size<30>;
509 0x7E => PUSH31 => stack::push::<31, H> => stack_io<0, 1>, imm_size<31>;
510 0x7F => PUSH32 => stack::push::<32, H> => stack_io<0, 1>, imm_size<32>;
511
512 0x80 => DUP1 => stack::dup::<1, H> => stack_io<1, 2>;
513 0x81 => DUP2 => stack::dup::<2, H> => stack_io<2, 3>;
514 0x82 => DUP3 => stack::dup::<3, H> => stack_io<3, 4>;
515 0x83 => DUP4 => stack::dup::<4, H> => stack_io<4, 5>;
516 0x84 => DUP5 => stack::dup::<5, H> => stack_io<5, 6>;
517 0x85 => DUP6 => stack::dup::<6, H> => stack_io<6, 7>;
518 0x86 => DUP7 => stack::dup::<7, H> => stack_io<7, 8>;
519 0x87 => DUP8 => stack::dup::<8, H> => stack_io<8, 9>;
520 0x88 => DUP9 => stack::dup::<9, H> => stack_io<9, 10>;
521 0x89 => DUP10 => stack::dup::<10, H> => stack_io<10, 11>;
522 0x8A => DUP11 => stack::dup::<11, H> => stack_io<11, 12>;
523 0x8B => DUP12 => stack::dup::<12, H> => stack_io<12, 13>;
524 0x8C => DUP13 => stack::dup::<13, H> => stack_io<13, 14>;
525 0x8D => DUP14 => stack::dup::<14, H> => stack_io<14, 15>;
526 0x8E => DUP15 => stack::dup::<15, H> => stack_io<15, 16>;
527 0x8F => DUP16 => stack::dup::<16, H> => stack_io<16, 17>;
528
529 0x90 => SWAP1 => stack::swap::<1, H> => stack_io<2, 2>;
530 0x91 => SWAP2 => stack::swap::<2, H> => stack_io<3, 3>;
531 0x92 => SWAP3 => stack::swap::<3, H> => stack_io<4, 4>;
532 0x93 => SWAP4 => stack::swap::<4, H> => stack_io<5, 5>;
533 0x94 => SWAP5 => stack::swap::<5, H> => stack_io<6, 6>;
534 0x95 => SWAP6 => stack::swap::<6, H> => stack_io<7, 7>;
535 0x96 => SWAP7 => stack::swap::<7, H> => stack_io<8, 8>;
536 0x97 => SWAP8 => stack::swap::<8, H> => stack_io<9, 9>;
537 0x98 => SWAP9 => stack::swap::<9, H> => stack_io<10, 10>;
538 0x99 => SWAP10 => stack::swap::<10, H> => stack_io<11, 11>;
539 0x9A => SWAP11 => stack::swap::<11, H> => stack_io<12, 12>;
540 0x9B => SWAP12 => stack::swap::<12, H> => stack_io<13, 13>;
541 0x9C => SWAP13 => stack::swap::<13, H> => stack_io<14, 14>;
542 0x9D => SWAP14 => stack::swap::<14, H> => stack_io<15, 15>;
543 0x9E => SWAP15 => stack::swap::<15, H> => stack_io<16, 16>;
544 0x9F => SWAP16 => stack::swap::<16, H> => stack_io<17, 17>;
545
546 0xA0 => LOG0 => host::log::<0, H> => stack_io<2, 0>;
547 0xA1 => LOG1 => host::log::<1, H> => stack_io<3, 0>;
548 0xA2 => LOG2 => host::log::<2, H> => stack_io<4, 0>;
549 0xA3 => LOG3 => host::log::<3, H> => stack_io<5, 0>;
550 0xA4 => LOG4 => host::log::<4, H> => stack_io<6, 0>;
551 0xD0 => DATALOAD => data::data_load => stack_io<1, 1>;
595 0xD1 => DATALOADN => data::data_loadn => stack_io<0, 1>, imm_size<2>;
596 0xD2 => DATASIZE => data::data_size => stack_io<0, 1>;
597 0xD3 => DATACOPY => data::data_copy => stack_io<3, 0>;
598 0xE0 => RJUMP => control::rjump => stack_io<0, 0>, imm_size<2>, terminating;
611 0xE1 => RJUMPI => control::rjumpi => stack_io<1, 0>, imm_size<2>;
612 0xE2 => RJUMPV => control::rjumpv => stack_io<1, 0>, imm_size<1>;
613 0xE3 => CALLF => control::callf => stack_io<0, 0>, imm_size<2>;
614 0xE4 => RETF => control::retf => stack_io<0, 0>, terminating;
615 0xE5 => JUMPF => control::jumpf => stack_io<0, 0>, imm_size<2>, terminating;
616 0xE6 => DUPN => stack::dupn => stack_io<0, 1>, imm_size<1>;
617 0xE7 => SWAPN => stack::swapn => stack_io<0, 0>, imm_size<1>;
618 0xE8 => EXCHANGE => stack::exchange => stack_io<0, 0>, imm_size<1>;
619 0xEC => EOFCREATE => contract::eofcreate::<H> => stack_io<4, 1>, imm_size<1>;
623 0xED => TXCREATE => contract::txcreate::<H> => stack_io<5, 1>;
624 0xEE => RETURNCONTRACT => contract::return_contract::<H> => stack_io<2, 0>, imm_size<1>, terminating;
625 0xF0 => CREATE => contract::create::<false, H, SPEC> => stack_io<3, 1>, not_eof;
627 0xF1 => CALL => contract::call::<H, SPEC> => stack_io<7, 1>, not_eof;
628 0xF2 => CALLCODE => contract::call_code::<H, SPEC> => stack_io<7, 1>, not_eof;
629 0xF3 => RETURN => control::ret => stack_io<2, 0>, terminating;
630 0xF4 => DELEGATECALL => contract::delegate_call::<H, SPEC> => stack_io<6, 1>, not_eof;
631 0xF5 => CREATE2 => contract::create::<true, H, SPEC> => stack_io<4, 1>, not_eof;
632 0xF7 => RETURNDATALOAD => system::returndataload::<H> => stack_io<1, 1>;
634 0xF8 => EXTCALL => contract::extcall::<H,SPEC> => stack_io<4, 1>;
635 0xF9 => EXFCALL => contract::extdcall::<H, SPEC> => stack_io<3, 1>;
636 0xFA => STATICCALL => contract::static_call::<H, SPEC> => stack_io<6, 1>, not_eof;
637 0xFB => EXTSCALL => contract::extscall::<H> => stack_io<3, 1>;
638 0xFD => REVERT => control::revert::<H, SPEC> => stack_io<2, 0>, terminating;
640 0xFE => INVALID => control::invalid => stack_io<0, 0>, terminating;
641 0xFF => SELFDESTRUCT => host::selfdestruct::<H, SPEC> => stack_io<1, 0>, not_eof, terminating;
642}
643
644#[cfg(test)]
645mod tests {
646 use super::*;
647
648 #[test]
649 fn test_opcode() {
650 let opcode = OpCode::new(0x00).unwrap();
651 assert!(!opcode.is_jumpdest());
652 assert!(!opcode.is_jump());
653 assert!(!opcode.is_push());
654 assert_eq!(opcode.as_str(), "STOP");
655 assert_eq!(opcode.get(), 0x00);
656 }
657
658 const REJECTED_IN_EOF: &[u8] = &[
659 0x38, 0x39, 0x3b, 0x3c, 0x3f, 0x5a, 0xf1, 0xf2, 0xf4, 0xfa, 0xff,
660 ];
661
662 #[test]
663 fn test_eof_disable() {
664 for opcode in REJECTED_IN_EOF.iter() {
665 let opcode = OpCode::new(*opcode).unwrap();
666 assert!(!opcode.info().is_eof, "Opcode {:?} is not EOF", opcode);
667 }
668 }
669
670 #[test]
671 fn test_imm_size() {
672 let mut opcodes = [0u8; 256];
673 for push in PUSH1..PUSH32 {
675 opcodes[push as usize] = push - PUSH1 + 1;
676 }
677 opcodes[DATALOADN as usize] = 2;
678 opcodes[RJUMP as usize] = 2;
679 opcodes[RJUMPI as usize] = 2;
680 opcodes[RJUMPV as usize] = 2;
681 opcodes[CALLF as usize] = 2;
682 opcodes[JUMPF as usize] = 2;
683 opcodes[DUPN as usize] = 1;
684 opcodes[SWAPN as usize] = 1;
685 opcodes[EXCHANGE as usize] = 1;
686 }
687
688 #[test]
689 fn test_enabled_opcodes() {
690 let opcodes = [
692 0x10..=0x1d,
693 0x20..=0x20,
694 0x30..=0x3f,
695 0x40..=0x48,
696 0x50..=0x5b,
697 0x54..=0x5f,
698 0x60..=0x6f,
699 0x70..=0x7f,
700 0x80..=0x8f,
701 0x90..=0x9f,
702 0xa0..=0xa4,
703 0xf0..=0xf5,
704 0xfa..=0xfa,
705 0xfd..=0xfd,
706 0xff..=0xff,
708 ];
709 for i in opcodes {
710 for opcode in i {
711 OpCode::new(opcode).expect("Opcode should be valid and enabled");
712 }
713 }
714 }
715
716 #[test]
717 fn test_terminating_opcodes() {
718 let terminating = [
719 RETF,
720 REVERT,
721 RETURN,
722 INVALID,
723 SELFDESTRUCT,
724 RETURNCONTRACT,
725 STOP,
726 RJUMP,
727 JUMPF,
728 ];
729 let mut opcodes = [false; 256];
730 for terminating in terminating.iter() {
731 opcodes[*terminating as usize] = true;
732 }
733
734 for (i, opcode) in OPCODE_INFO_JUMPTABLE.into_iter().enumerate() {
735 assert_eq!(
736 opcode
737 .map(|opcode| opcode.is_terminating_opcode)
738 .unwrap_or_default(),
739 opcodes[i],
740 "Opcode {:?} terminating chack failed.",
741 opcode
742 );
743 }
744 }
745}