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(specification::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().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()
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
130macro_rules! popn {
131    ([ $($x:ident),* ],$interpreterreter:expr $(,$ret:expr)? ) => {
132        let Some([$( $x ),*]) = $interpreterreter.stack.popn() else {
133            $interpreterreter.control.set_instruction_result($crate::InstructionResult::StackUnderflow);
134            return $($ret)?;
135        };
136    };
137}
138
139macro_rules! popn_top {
140    ([ $($x:ident),* ], $top:ident, $interpreterreter:expr $(,$ret:expr)? ) => {
141        let Some(([$( $x ),*], $top)) = $interpreterreter.stack.popn_top() else {
142            $interpreterreter.control.set_instruction_result($crate::InstructionResult::StackUnderflow);
143            return $($ret)?;
144        };
145    };
146}
147
148/// Pushes a `B256` value onto the stack. Fails the instruction if the stack is full.
149#[macro_export]
150macro_rules! push {
151    ($interpreter:expr, $x:expr $(,$ret:item)?) => (
152        if !($interpreter.stack.push($x)) {
153            $interpreter.control.set_instruction_result($crate::InstructionResult::StackOverflow);
154            return $($ret)?;
155        }
156    )
157}
158
159/// Converts a `U256` value to a `u64`, saturating to `MAX` if the value is too large.
160#[macro_export]
161macro_rules! as_u64_saturated {
162    ($v:expr) => {
163        match $v.as_limbs() {
164            x => {
165                if (x[1] == 0) & (x[2] == 0) & (x[3] == 0) {
166                    x[0]
167                } else {
168                    u64::MAX
169                }
170            }
171        }
172    };
173}
174
175/// Converts a `U256` value to a `usize`, saturating to `MAX` if the value is too large.
176#[macro_export]
177macro_rules! as_usize_saturated {
178    ($v:expr) => {
179        usize::try_from($crate::as_u64_saturated!($v)).unwrap_or(usize::MAX)
180    };
181}
182
183/// Converts a `U256` value to a `isize`, saturating to `isize::MAX` if the value is too large.
184#[macro_export]
185macro_rules! as_isize_saturated {
186    ($v:expr) => {
187        // `isize_try_from(u64::MAX)`` will fail and return isize::MAX
188        // This is expected behavior as we are saturating the value.
189        isize::try_from($crate::as_u64_saturated!($v)).unwrap_or(isize::MAX)
190    };
191}
192
193/// Converts a `U256` value to a `usize`, failing the instruction if the value is too large.
194#[macro_export]
195macro_rules! as_usize_or_fail {
196    ($interpreter:expr, $v:expr) => {
197        $crate::as_usize_or_fail_ret!($interpreter, $v, ())
198    };
199    ($interpreter:expr, $v:expr, $reason:expr) => {
200        $crate::as_usize_or_fail_ret!($interpreter, $v, $reason, ())
201    };
202}
203
204/// Converts a `U256` value to a `usize` and returns `ret`,
205/// failing the instruction if the value is too large.
206#[macro_export]
207macro_rules! as_usize_or_fail_ret {
208    ($interpreter:expr, $v:expr, $ret:expr) => {
209        $crate::as_usize_or_fail_ret!(
210            $interpreter,
211            $v,
212            $crate::InstructionResult::InvalidOperandOOG,
213            $ret
214        )
215    };
216
217    ($interpreter:expr, $v:expr, $reason:expr, $ret:expr) => {
218        match $v.as_limbs() {
219            x => {
220                if (x[0] > usize::MAX as u64) | (x[1] != 0) | (x[2] != 0) | (x[3] != 0) {
221                    $interpreter.control.set_instruction_result($reason);
222                    return $ret;
223                }
224                x[0] as usize
225            }
226        }
227    };
228}