Skip to main content

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]
5#[collapse_debuginfo(yes)]
6macro_rules! require_non_staticcall {
7    ($interpreter:expr) => {
8        if $interpreter.runtime_flag.is_static() {
9            $interpreter.halt($crate::InstructionResult::StateChangeDuringStaticCall);
10            return;
11        }
12    };
13}
14
15/// Check if the `SPEC` is enabled, and fail the instruction if it is not.
16#[macro_export]
17#[collapse_debuginfo(yes)]
18macro_rules! check {
19    ($interpreter:expr, $min:ident) => {
20        if !$interpreter
21            .runtime_flag
22            .spec_id()
23            .is_enabled_in(primitives::hardfork::SpecId::$min)
24        {
25            $interpreter.halt_not_activated();
26            return;
27        }
28    };
29}
30
31/// Records a `gas` cost and fails the instruction if it would exceed the available gas.
32#[macro_export]
33#[collapse_debuginfo(yes)]
34macro_rules! gas {
35    ($interpreter:expr, $gas:expr) => {
36        $crate::gas!($interpreter, $gas, ())
37    };
38    ($interpreter:expr, $gas:expr, $ret:expr) => {
39        if !$interpreter.gas.record_cost($gas) {
40            $interpreter.halt_oog();
41            return $ret;
42        }
43    };
44}
45
46/// Loads account and account berlin gas cost accounting.
47#[macro_export]
48#[collapse_debuginfo(yes)]
49macro_rules! berlin_load_account {
50    ($context:expr, $address:expr, $load_code:expr) => {
51        $crate::berlin_load_account!($context, $address, $load_code, ())
52    };
53    ($context:expr, $address:expr, $load_code:expr, $ret:expr) => {{
54        let cold_load_gas = $context.host.gas_params().cold_account_additional_cost();
55        let skip_cold_load = $context.interpreter.gas.remaining() < cold_load_gas;
56        match $context
57            .host
58            .load_account_info_skip_cold_load($address, $load_code, skip_cold_load)
59        {
60            Ok(account) => {
61                if account.is_cold {
62                    $crate::gas!($context.interpreter, cold_load_gas, $ret);
63                }
64                account
65            }
66            Err(LoadError::ColdLoadSkipped) => {
67                $context.interpreter.halt_oog();
68                return $ret;
69            }
70            Err(LoadError::DBError) => {
71                $context.interpreter.halt_fatal();
72                return $ret;
73            }
74        }
75    }};
76}
77
78/// Resizes the interpreter memory if necessary. Fails the instruction if the memory or gas limit
79/// is exceeded.
80#[macro_export]
81#[collapse_debuginfo(yes)]
82macro_rules! resize_memory {
83    ($interpreter:expr, $gas_params:expr, $offset:expr, $len:expr) => {
84        $crate::resize_memory!($interpreter, $gas_params, $offset, $len, ())
85    };
86    ($interpreter:expr, $gas_params:expr, $offset:expr, $len:expr, $ret:expr) => {
87        if let Err(result) = $crate::interpreter::resize_memory(
88            &mut $interpreter.gas,
89            &mut $interpreter.memory,
90            $gas_params,
91            $offset,
92            $len,
93        ) {
94            $interpreter.halt(result);
95            return $ret;
96        }
97    };
98}
99
100/// Pops n values from the stack. Fails the instruction if n values can't be popped.
101#[macro_export]
102#[collapse_debuginfo(yes)]
103macro_rules! popn {
104    ([ $($x:ident),* ],$interpreter:expr $(,$ret:expr)? ) => {
105        let Some([$( $x ),*]) = $interpreter.stack.popn() else {
106            $interpreter.halt_underflow();
107            return $($ret)?;
108        };
109    };
110}
111
112#[doc(hidden)]
113#[macro_export]
114#[collapse_debuginfo(yes)]
115macro_rules! _count {
116    (@count) => { 0 };
117    (@count $head:tt $($tail:tt)*) => { 1 + _count!(@count $($tail)*) };
118    ($($arg:tt)*) => { _count!(@count $($arg)*) };
119}
120
121/// Pops n values from the stack and returns the top value. Fails the instruction if n values can't be popped.
122#[macro_export]
123#[collapse_debuginfo(yes)]
124macro_rules! popn_top {
125    ([ $($x:ident),* ], $top:ident, $interpreter:expr $(,$ret:expr)? ) => {
126        /*
127        let Some(([$( $x ),*], $top)) = $interpreter.stack.popn_top() else {
128            $interpreter.halt($crate::InstructionResult::StackUnderflow);
129            return $($ret)?;
130        };
131        */
132
133        // Workaround for https://github.com/rust-lang/rust/issues/144329.
134        if $interpreter.stack.len() < (1 + $crate::_count!($($x)*)) {
135            $interpreter.halt_underflow();
136            return $($ret)?;
137        }
138        let ([$( $x ),*], $top) = unsafe { $interpreter.stack.popn_top().unwrap_unchecked() };
139    };
140}
141
142/// Pushes a `B256` value onto the stack. Fails the instruction if the stack is full.
143#[macro_export]
144#[collapse_debuginfo(yes)]
145macro_rules! push {
146    ($interpreter:expr, $x:expr $(,$ret:item)?) => (
147        if !($interpreter.stack.push($x)) {
148            $interpreter.halt_overflow();
149            return $($ret)?;
150        }
151    )
152}
153
154/// Converts a `U256` value to a `u64`, saturating to `MAX` if the value is too large.
155#[macro_export]
156#[collapse_debuginfo(yes)]
157macro_rules! as_u64_saturated {
158    ($v:expr) => {
159        u64::try_from($v).unwrap_or(u64::MAX)
160    };
161}
162
163/// Converts a `U256` value to a `usize`, saturating to `MAX` if the value is too large.
164#[macro_export]
165#[collapse_debuginfo(yes)]
166macro_rules! as_usize_saturated {
167    ($v:expr) => {
168        usize::try_from($v).unwrap_or(usize::MAX)
169    };
170}
171
172/// Converts a `U256` value to a `isize`, saturating to `isize::MAX` if the value is too large.
173#[macro_export]
174#[collapse_debuginfo(yes)]
175macro_rules! as_isize_saturated {
176    ($v:expr) => {
177        isize::try_from($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]
183#[collapse_debuginfo(yes)]
184macro_rules! as_usize_or_fail {
185    ($interpreter:expr, $v:expr) => {
186        $crate::as_usize_or_fail_ret!($interpreter, $v, ())
187    };
188    ($interpreter:expr, $v:expr, $reason:expr) => {
189        $crate::as_usize_or_fail_ret!($interpreter, $v, $reason, ())
190    };
191}
192
193/// Converts a `U256` value to a `usize` and returns `ret`,
194/// failing the instruction if the value is too large.
195#[macro_export]
196#[collapse_debuginfo(yes)]
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}