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