Skip to main content

revm_interpreter/instructions/
macros.rs

1//! Utility macros to help implementing opcode instruction functions.
2
3/// Fails the instruction if the current call is static.
4#[macro_export]
5#[collapse_debuginfo(yes)]
6macro_rules! require_non_staticcall {
7    ($interpreter:expr) => {
8        if $interpreter.runtime_flag.is_static() {
9            $crate::primitives::hints_util::cold_path();
10            return Err($crate::InstructionResult::StateChangeDuringStaticCall);
11        }
12    };
13}
14
15/// Check if the `SPEC` is enabled, and fail the instruction if it is not.
16#[macro_export]
17#[collapse_debuginfo(yes)]
18macro_rules! check {
19    ($interpreter:expr, $min:ident) => {
20        if !$interpreter
21            .runtime_flag
22            .spec_id()
23            .is_enabled_in(primitives::hardfork::SpecId::$min)
24        {
25            $crate::primitives::hints_util::cold_path();
26            return Err($crate::InstructionResult::NotActivated);
27        }
28    };
29}
30
31/// Records a state gas cost (EIP-8037) and fails the instruction if it would exceed the available gas.
32/// State gas only deducts from `remaining` (not `regular_gas_remaining`).
33#[macro_export]
34#[collapse_debuginfo(yes)]
35macro_rules! state_gas {
36    ($interpreter:expr, $gas:expr) => {{
37        if !$interpreter.gas.record_state_cost($gas) {
38            $crate::primitives::hints_util::cold_path();
39            return Err($crate::InstructionResult::OutOfGas);
40        }
41    }};
42}
43
44/// Records a `gas` cost and fails the instruction if it would exceed the available gas.
45#[macro_export]
46#[collapse_debuginfo(yes)]
47macro_rules! gas {
48    ($interpreter:expr, $gas:expr) => {
49        if !$interpreter.gas.record_regular_cost($gas) {
50            $crate::primitives::hints_util::cold_path();
51            return Err($crate::InstructionResult::OutOfGas);
52        }
53    };
54}
55
56/// Pops n values from the stack. Fails the instruction if n values can't be popped.
57#[macro_export]
58#[collapse_debuginfo(yes)]
59macro_rules! popn {
60    ([ $($x:ident),* ],$interpreter:expr) => {
61        let Some([$( $x ),*]) = $interpreter.stack.popn() else {
62            $crate::primitives::hints_util::cold_path();
63            return Err($crate::InstructionResult::StackUnderflow);
64        };
65    };
66}
67
68#[doc(hidden)]
69#[macro_export]
70#[collapse_debuginfo(yes)]
71macro_rules! _count {
72    (@count) => { 0 };
73    (@count $head:tt $($tail:tt)*) => { 1 + $crate::_count!(@count $($tail)*) };
74    ($($arg:tt)*) => { $crate::_count!(@count $($arg)*) };
75}
76
77/// Pops n values from the stack and returns the top value. Fails the instruction if n values can't be popped.
78#[macro_export]
79#[collapse_debuginfo(yes)]
80macro_rules! popn_top {
81    ([ $($x:ident),* ], $top:ident, $interpreter:expr) => {
82        /*
83        let Some(([$( $x ),*], $top)) = $interpreter.stack.popn_top() else {
84            $crate::primitives::hints_util::cold_path();
85            return Err($crate::InstructionResult::StackUnderflow);
86        };
87        */
88
89        // Workaround for https://github.com/rust-lang/rust/issues/144329.
90        if $interpreter.stack.len() < (1 + $crate::_count!($($x)*)) {
91            $crate::primitives::hints_util::cold_path();
92            return Err($crate::InstructionResult::StackUnderflow);
93        }
94        let ([$( $x ),*], $top) = unsafe { $crate::interpreter_types::StackTr::popn_top(&mut $interpreter.stack).unwrap_unchecked() };
95    };
96}
97
98/// Pushes a `B256` value onto the stack. Fails the instruction if the stack is full.
99#[macro_export]
100#[collapse_debuginfo(yes)]
101macro_rules! push {
102    ($interpreter:expr, $x:expr) => {
103        if !$interpreter.stack.push($x) {
104            $crate::primitives::hints_util::cold_path();
105            return Err($crate::InstructionResult::StackOverflow);
106        }
107    };
108}
109
110/// Converts a `U256` value to a `u64`, saturating to `MAX` if the value is too large.
111#[macro_export]
112#[collapse_debuginfo(yes)]
113macro_rules! as_u64_saturated {
114    ($v:expr) => {
115        u64::try_from($v).unwrap_or(u64::MAX)
116    };
117}
118
119/// Converts a `U256` value to a `usize`, saturating to `MAX` if the value is too large.
120#[macro_export]
121#[collapse_debuginfo(yes)]
122macro_rules! as_usize_saturated {
123    ($v:expr) => {
124        usize::try_from($v).unwrap_or(usize::MAX)
125    };
126}
127
128/// Converts a `U256` value to a `usize`, failing the instruction if the value is too large.
129#[macro_export]
130#[collapse_debuginfo(yes)]
131macro_rules! as_usize_or_fail {
132    ($interpreter:expr, $v:expr) => {
133        match $v.as_limbs() {
134            x => {
135                if (x[0] > usize::MAX as u64) | (x[1] != 0) | (x[2] != 0) | (x[3] != 0) {
136                    $crate::primitives::hints_util::cold_path();
137                    return Err($crate::InstructionResult::InvalidOperandOOG);
138                }
139                x[0] as usize
140            }
141        }
142    };
143}