revm_interpreter/instructions/
macros.rs

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