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