revm_interpreter/instructions/
macros.rs

1//! Utility macros to help implementing opcode instruction functions.
2
3/// Fails the instruction if the current call is static.
4#[macro_export]
5macro_rules! require_non_staticcall {
6    ($interp:expr) => {
7        if $interp.is_static {
8            $interp.instruction_result = $crate::InstructionResult::StateChangeDuringStaticCall;
9            return;
10        }
11    };
12}
13
14/// Error if the current call is executing EOF.
15#[macro_export]
16macro_rules! require_eof {
17    ($interp:expr) => {
18        if !$interp.is_eof {
19            $interp.instruction_result = $crate::InstructionResult::EOFOpcodeDisabledInLegacy;
20            return;
21        }
22    };
23}
24
25/// Error if not init eof call.
26#[macro_export]
27macro_rules! require_init_eof {
28    ($interp:expr) => {
29        if !$interp.is_eof_init {
30            $interp.instruction_result = $crate::InstructionResult::ReturnContractInNotInitEOF;
31            return;
32        }
33    };
34}
35
36/// Check if the `SPEC` is enabled, and fail the instruction if it is not.
37#[macro_export]
38macro_rules! check {
39    ($interp:expr, $min:ident) => {
40        if const {
41            !<SPEC as $crate::primitives::Spec>::SPEC_ID
42                .is_enabled_in($crate::primitives::SpecId::$min)
43        } {
44            $interp.instruction_result = $crate::InstructionResult::NotActivated;
45            return;
46        }
47    };
48}
49
50/// Records a `gas` cost and fails the instruction if it would exceed the available gas.
51#[macro_export]
52macro_rules! gas {
53    ($interp:expr, $gas:expr) => {
54        $crate::gas!($interp, $gas, ())
55    };
56    ($interp:expr, $gas:expr, $ret:expr) => {
57        if !$interp.gas.record_cost($gas) {
58            $interp.instruction_result = $crate::InstructionResult::OutOfGas;
59            return $ret;
60        }
61    };
62}
63
64/// Records a `gas` refund.
65#[macro_export]
66macro_rules! refund {
67    ($interp:expr, $gas:expr) => {
68        $interp.gas.record_refund($gas)
69    };
70}
71
72/// Same as [`gas!`], but with `gas` as an option.
73#[macro_export]
74macro_rules! gas_or_fail {
75    ($interp:expr, $gas:expr) => {
76        match $gas {
77            Some(gas_used) => $crate::gas!($interp, gas_used),
78            None => {
79                $interp.instruction_result = $crate::InstructionResult::OutOfGas;
80                return;
81            }
82        }
83    };
84}
85
86/// Resizes the interpreter memory if necessary. Fails the instruction if the memory or gas limit
87/// is exceeded.
88#[macro_export]
89macro_rules! resize_memory {
90    ($interp:expr, $offset:expr, $len:expr) => {
91        $crate::resize_memory!($interp, $offset, $len, ())
92    };
93    ($interp:expr, $offset:expr, $len:expr, $ret:expr) => {
94        let new_size = $offset.saturating_add($len);
95        if new_size > $interp.shared_memory.len() {
96            #[cfg(feature = "memory_limit")]
97            if $interp.shared_memory.limit_reached(new_size) {
98                $interp.instruction_result = $crate::InstructionResult::MemoryLimitOOG;
99                return $ret;
100            }
101
102            // Note: we can't use `Interpreter` directly here because of potential double-borrows.
103            if !$crate::interpreter::resize_memory(
104                &mut $interp.shared_memory,
105                &mut $interp.gas,
106                new_size,
107            ) {
108                $interp.instruction_result = $crate::InstructionResult::MemoryOOG;
109                return $ret;
110            }
111        }
112    };
113}
114
115/// Pops `Address` values from the stack. Fails the instruction if the stack is too small.
116#[macro_export]
117macro_rules! pop_address {
118    ($interp:expr, $x1:ident) => {
119        $crate::pop_address_ret!($interp, $x1, ())
120    };
121    ($interp:expr, $x1:ident, $x2:ident) => {
122        $crate::pop_address_ret!($interp, $x1, $x2, ())
123    };
124}
125
126/// Pop `Address` values from the stack, returns `ret` on stack underflow.
127#[macro_export]
128macro_rules! pop_address_ret {
129    ($interp:expr, $x1:ident, $ret:expr) => {
130        if $interp.stack.len() < 1 {
131            $interp.instruction_result = $crate::InstructionResult::StackUnderflow;
132            return $ret;
133        }
134        // SAFETY: Length is checked above.
135        let $x1 = $crate::primitives::Address::from_word($crate::primitives::B256::from(unsafe {
136            $interp.stack.pop_unsafe()
137        }));
138    };
139    ($interp:expr, $x1:ident, $x2:ident, $ret:expr) => {
140        if $interp.stack.len() < 2 {
141            $interp.instruction_result = $crate::InstructionResult::StackUnderflow;
142            return $ret;
143        }
144        // SAFETY: Length is checked above.
145        let $x1 = $crate::primitives::Address::from_word($crate::primitives::B256::from(unsafe {
146            $interp.stack.pop_unsafe()
147        }));
148        let $x2 = $crate::primitives::Address::from_word($crate::primitives::B256::from(unsafe {
149            $interp.stack.pop_unsafe()
150        }));
151    };
152}
153
154/// Pops `U256` values from the stack. Fails the instruction if the stack is too small.
155#[macro_export]
156macro_rules! pop {
157    ($interp:expr, $x1:ident) => {
158        $crate::pop_ret!($interp, $x1, ())
159    };
160    ($interp:expr, $x1:ident, $x2:ident) => {
161        $crate::pop_ret!($interp, $x1, $x2, ())
162    };
163    ($interp:expr, $x1:ident, $x2:ident, $x3:ident) => {
164        $crate::pop_ret!($interp, $x1, $x2, $x3, ())
165    };
166    ($interp:expr, $x1:ident, $x2:ident, $x3:ident, $x4:ident) => {
167        $crate::pop_ret!($interp, $x1, $x2, $x3, $x4, ())
168    };
169    ($interp:expr, $x1:ident, $x2:ident, $x3:ident, $x4:ident, $x5:ident) => {
170        $crate::pop_ret!($interp, $x1, $x2, $x3, $x4, $x5, ())
171    };
172}
173
174/// Pops `U256` values from the stack, and returns `ret`.
175/// Fails the instruction if the stack is too small.
176#[macro_export]
177macro_rules! pop_ret {
178    ($interp:expr, $x1:ident, $ret:expr) => {
179        if $interp.stack.len() < 1 {
180            $interp.instruction_result = $crate::InstructionResult::StackUnderflow;
181            return $ret;
182        }
183        // SAFETY: Length is checked above.
184        let $x1 = unsafe { $interp.stack.pop_unsafe() };
185    };
186    ($interp:expr, $x1:ident, $x2:ident, $ret:expr) => {
187        if $interp.stack.len() < 2 {
188            $interp.instruction_result = $crate::InstructionResult::StackUnderflow;
189            return $ret;
190        }
191        // SAFETY: Length is checked above.
192        let ($x1, $x2) = unsafe { $interp.stack.pop2_unsafe() };
193    };
194    ($interp:expr, $x1:ident, $x2:ident, $x3:ident, $ret:expr) => {
195        if $interp.stack.len() < 3 {
196            $interp.instruction_result = $crate::InstructionResult::StackUnderflow;
197            return $ret;
198        }
199        // SAFETY: Length is checked above.
200        let ($x1, $x2, $x3) = unsafe { $interp.stack.pop3_unsafe() };
201    };
202    ($interp:expr, $x1:ident, $x2:ident, $x3:ident, $x4:ident, $ret:expr) => {
203        if $interp.stack.len() < 4 {
204            $interp.instruction_result = $crate::InstructionResult::StackUnderflow;
205            return $ret;
206        }
207        // SAFETY: Length is checked above.
208        let ($x1, $x2, $x3, $x4) = unsafe { $interp.stack.pop4_unsafe() };
209    };
210    ($interp:expr, $x1:ident, $x2:ident, $x3:ident, $x4:ident, $x5:ident, $ret:expr) => {
211        if $interp.stack.len() < 5 {
212            $interp.instruction_result = $crate::InstructionResult::StackUnderflow;
213            return $ret;
214        }
215        // SAFETY: Length is checked above.
216        let ($x1, $x2, $x3, $x4, $x5) = unsafe { $interp.stack.pop5_unsafe() };
217    };
218}
219
220/// Pops `U256` values from the stack, and returns a reference to the top of the stack.
221/// Fails the instruction if the stack is too small.
222#[macro_export]
223macro_rules! pop_top {
224    ($interp:expr, $x1:ident) => {
225        if $interp.stack.len() < 1 {
226            $interp.instruction_result = $crate::InstructionResult::StackUnderflow;
227            return;
228        }
229        // SAFETY: Length is checked above.
230        let $x1 = unsafe { $interp.stack.top_unsafe() };
231    };
232    ($interp:expr, $x1:ident, $x2:ident) => {
233        if $interp.stack.len() < 2 {
234            $interp.instruction_result = $crate::InstructionResult::StackUnderflow;
235            return;
236        }
237        // SAFETY: Length is checked above.
238        let ($x1, $x2) = unsafe { $interp.stack.pop_top_unsafe() };
239    };
240    ($interp:expr, $x1:ident, $x2:ident, $x3:ident) => {
241        if $interp.stack.len() < 3 {
242            $interp.instruction_result = $crate::InstructionResult::StackUnderflow;
243            return;
244        }
245        // SAFETY: Length is checked above.
246        let ($x1, $x2, $x3) = unsafe { $interp.stack.pop2_top_unsafe() };
247    };
248}
249
250/// Pushes `B256` values onto the stack. Fails the instruction if the stack is full.
251#[macro_export]
252macro_rules! push_b256 {
253	($interp:expr, $($x:expr),* $(,)?) => ($(
254        match $interp.stack.push_b256($x) {
255            Ok(()) => {},
256            Err(e) => {
257                $interp.instruction_result = e;
258                return;
259            },
260        }
261    )*)
262}
263
264/// Pushes a `B256` value onto the stack. Fails the instruction if the stack is full.
265#[macro_export]
266macro_rules! push {
267    ($interp:expr, $($x:expr),* $(,)?) => ($(
268        match $interp.stack.push($x) {
269            Ok(()) => {},
270            Err(e) => {
271                $interp.instruction_result = e;
272                return;
273            }
274        }
275    )*)
276}
277
278/// Converts a `U256` value to a `u64`, saturating to `MAX` if the value is too large.
279#[macro_export]
280macro_rules! as_u64_saturated {
281    ($v:expr) => {
282        match $v.as_limbs() {
283            x => {
284                if (x[1] == 0) & (x[2] == 0) & (x[3] == 0) {
285                    x[0]
286                } else {
287                    u64::MAX
288                }
289            }
290        }
291    };
292}
293
294/// Converts a `U256` value to a `usize`, saturating to `MAX` if the value is too large.
295#[macro_export]
296macro_rules! as_usize_saturated {
297    ($v:expr) => {
298        usize::try_from($crate::as_u64_saturated!($v)).unwrap_or(usize::MAX)
299    };
300}
301
302/// Converts a `U256` value to a `isize`, saturating to `isize::MAX` if the value is too large.
303#[macro_export]
304macro_rules! as_isize_saturated {
305    ($v:expr) => {
306        // `isize_try_from(u64::MAX)`` will fail and return isize::MAX
307        // this is expected behavior as we are saturating the value.
308        isize::try_from($crate::as_u64_saturated!($v)).unwrap_or(isize::MAX)
309    };
310}
311
312/// Converts a `U256` value to a `usize`, failing the instruction if the value is too large.
313#[macro_export]
314macro_rules! as_usize_or_fail {
315    ($interp:expr, $v:expr) => {
316        $crate::as_usize_or_fail_ret!($interp, $v, ())
317    };
318    ($interp:expr, $v:expr, $reason:expr) => {
319        $crate::as_usize_or_fail_ret!($interp, $v, $reason, ())
320    };
321}
322
323/// Converts a `U256` value to a `usize` and returns `ret`,
324/// failing the instruction if the value is too large.
325#[macro_export]
326macro_rules! as_usize_or_fail_ret {
327    ($interp:expr, $v:expr, $ret:expr) => {
328        $crate::as_usize_or_fail_ret!(
329            $interp,
330            $v,
331            $crate::InstructionResult::InvalidOperandOOG,
332            $ret
333        )
334    };
335
336    ($interp:expr, $v:expr, $reason:expr, $ret:expr) => {
337        match $v.as_limbs() {
338            x => {
339                if (x[0] > usize::MAX as u64) | (x[1] != 0) | (x[2] != 0) | (x[3] != 0) {
340                    $interp.instruction_result = $reason;
341                    return $ret;
342                }
343                x[0] as usize
344            }
345        }
346    };
347}