revm_interpreter/instructions/
macros.rs

1//! Utility macros to help implementing opcode instruction functions.
2
3/// `const` Option `?`.
4#[macro_export]
5#[collapse_debuginfo(yes)]
6macro_rules! tri {
7    ($e:expr) => {
8        match $e {
9            Some(v) => v,
10            None => return None,
11        }
12    };
13}
14
15/// Fails the instruction if the current call is static.
16#[macro_export]
17#[collapse_debuginfo(yes)]
18macro_rules! require_non_staticcall {
19    ($interpreter:expr) => {
20        if $interpreter.runtime_flag.is_static() {
21            $interpreter.halt($crate::InstructionResult::StateChangeDuringStaticCall);
22            return;
23        }
24    };
25}
26
27/// Macro for optional try - returns early if the expression evaluates to None.
28/// Similar to the `?` operator but for use in instruction implementations.
29#[macro_export]
30#[collapse_debuginfo(yes)]
31macro_rules! otry {
32    ($expression: expr) => {{
33        let Some(value) = $expression else {
34            return;
35        };
36        value
37    }};
38}
39
40/// Check if the `SPEC` is enabled, and fail the instruction if it is not.
41#[macro_export]
42#[collapse_debuginfo(yes)]
43macro_rules! check {
44    ($interpreter:expr, $min:ident) => {
45        if !$interpreter
46            .runtime_flag
47            .spec_id()
48            .is_enabled_in(primitives::hardfork::SpecId::$min)
49        {
50            $interpreter.halt_not_activated();
51            return;
52        }
53    };
54}
55
56/// Records a `gas` cost and fails the instruction if it would exceed the available gas.
57#[macro_export]
58#[collapse_debuginfo(yes)]
59macro_rules! gas {
60    ($interpreter:expr, $gas:expr) => {
61        $crate::gas!($interpreter, $gas, ())
62    };
63    ($interpreter:expr, $gas:expr, $ret:expr) => {
64        if !$interpreter.gas.record_cost($gas) {
65            $interpreter.halt_oog();
66            return $ret;
67        }
68    };
69}
70
71/// Same as [`gas!`], but with `gas` as an option.
72#[macro_export]
73#[collapse_debuginfo(yes)]
74macro_rules! gas_or_fail {
75    ($interpreter:expr, $gas:expr) => {
76        $crate::gas_or_fail!($interpreter, $gas, ())
77    };
78    ($interpreter:expr, $gas:expr, $ret:expr) => {
79        match $gas {
80            Some(gas_used) => $crate::gas!($interpreter, gas_used, $ret),
81            None => {
82                $interpreter.halt_oog();
83                return $ret;
84            }
85        }
86    };
87}
88
89/// Resizes the interpreter memory if necessary. Fails the instruction if the memory or gas limit
90/// is exceeded.
91#[macro_export]
92#[collapse_debuginfo(yes)]
93macro_rules! resize_memory {
94    ($interpreter:expr, $offset:expr, $len:expr) => {
95        $crate::resize_memory!($interpreter, $offset, $len, ())
96    };
97    ($interpreter:expr, $offset:expr, $len:expr, $ret:expr) => {
98        if !$crate::interpreter::resize_memory(
99            &mut $interpreter.gas,
100            &mut $interpreter.memory,
101            $offset,
102            $len,
103        ) {
104            $interpreter.halt_memory_oog();
105            return $ret;
106        }
107    };
108}
109
110/// Pops n values from the stack. Fails the instruction if n values can't be popped.
111#[macro_export]
112#[collapse_debuginfo(yes)]
113macro_rules! popn {
114    ([ $($x:ident),* ],$interpreter:expr $(,$ret:expr)? ) => {
115        let Some([$( $x ),*]) = $interpreter.stack.popn() else {
116            $interpreter.halt_underflow();
117            return $($ret)?;
118        };
119    };
120}
121
122#[doc(hidden)]
123#[macro_export]
124#[collapse_debuginfo(yes)]
125macro_rules! _count {
126    (@count) => { 0 };
127    (@count $head:tt $($tail:tt)*) => { 1 + _count!(@count $($tail)*) };
128    ($($arg:tt)*) => { _count!(@count $($arg)*) };
129}
130
131/// Pops n values from the stack and returns the top value. Fails the instruction if n values can't be popped.
132#[macro_export]
133#[collapse_debuginfo(yes)]
134macro_rules! popn_top {
135    ([ $($x:ident),* ], $top:ident, $interpreter:expr $(,$ret:expr)? ) => {
136        /*
137        let Some(([$( $x ),*], $top)) = $interpreter.stack.popn_top() else {
138            $interpreter.halt($crate::InstructionResult::StackUnderflow);
139            return $($ret)?;
140        };
141        */
142
143        // Workaround for https://github.com/rust-lang/rust/issues/144329.
144        if $interpreter.stack.len() < (1 + $crate::_count!($($x)*)) {
145            $interpreter.halt_underflow();
146            return $($ret)?;
147        }
148        let ([$( $x ),*], $top) = unsafe { $interpreter.stack.popn_top().unwrap_unchecked() };
149    };
150}
151
152/// Pushes a `B256` value onto the stack. Fails the instruction if the stack is full.
153#[macro_export]
154#[collapse_debuginfo(yes)]
155macro_rules! push {
156    ($interpreter:expr, $x:expr $(,$ret:item)?) => (
157        if !($interpreter.stack.push($x)) {
158            $interpreter.halt_overflow();
159            return $($ret)?;
160        }
161    )
162}
163
164/// Converts a `U256` value to a `u64`, saturating to `MAX` if the value is too large.
165#[macro_export]
166#[collapse_debuginfo(yes)]
167macro_rules! as_u64_saturated {
168    ($v:expr) => {
169        match $v.as_limbs() {
170            x => {
171                if (x[1] == 0) & (x[2] == 0) & (x[3] == 0) {
172                    x[0]
173                } else {
174                    u64::MAX
175                }
176            }
177        }
178    };
179}
180
181/// Converts a `U256` value to a `usize`, saturating to `MAX` if the value is too large.
182#[macro_export]
183#[collapse_debuginfo(yes)]
184macro_rules! as_usize_saturated {
185    ($v:expr) => {
186        usize::try_from($crate::as_u64_saturated!($v)).unwrap_or(usize::MAX)
187    };
188}
189
190/// Converts a `U256` value to a `isize`, saturating to `isize::MAX` if the value is too large.
191#[macro_export]
192#[collapse_debuginfo(yes)]
193macro_rules! as_isize_saturated {
194    ($v:expr) => {
195        // `isize_try_from(u64::MAX)`` will fail and return isize::MAX
196        // This is expected behavior as we are saturating the value.
197        isize::try_from($crate::as_u64_saturated!($v)).unwrap_or(isize::MAX)
198    };
199}
200
201/// Converts a `U256` value to a `usize`, failing the instruction if the value is too large.
202#[macro_export]
203#[collapse_debuginfo(yes)]
204macro_rules! as_usize_or_fail {
205    ($interpreter:expr, $v:expr) => {
206        $crate::as_usize_or_fail_ret!($interpreter, $v, ())
207    };
208    ($interpreter:expr, $v:expr, $reason:expr) => {
209        $crate::as_usize_or_fail_ret!($interpreter, $v, $reason, ())
210    };
211}
212
213/// Converts a `U256` value to a `usize` and returns `ret`,
214/// failing the instruction if the value is too large.
215#[macro_export]
216#[collapse_debuginfo(yes)]
217macro_rules! as_usize_or_fail_ret {
218    ($interpreter:expr, $v:expr, $ret:expr) => {
219        $crate::as_usize_or_fail_ret!(
220            $interpreter,
221            $v,
222            $crate::InstructionResult::InvalidOperandOOG,
223            $ret
224        )
225    };
226
227    ($interpreter:expr, $v:expr, $reason:expr, $ret:expr) => {
228        match $v.as_limbs() {
229            x => {
230                if (x[0] > usize::MAX as u64) | (x[1] != 0) | (x[2] != 0) | (x[3] != 0) {
231                    $interpreter.halt($reason);
232                    return $ret;
233                }
234                x[0] as usize
235            }
236        }
237    };
238}