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 state gas cost (EIP-8037) and fails the instruction if it would exceed the available gas.
32/// State gas only deducts from `remaining` (not `regular_gas_remaining`).
33#[macro_export]
34#[collapse_debuginfo(yes)]
35macro_rules! state_gas {
36    ($interpreter:expr, $gas:expr) => {{
37        if !$interpreter.gas.record_state_cost($gas) {
38            $interpreter.halt_oog();
39            return;
40        }
41    }};
42    ($interpreter:expr, $gas:expr, $ret:expr) => {{
43        if !$interpreter.gas.record_state_cost($gas) {
44            $interpreter.halt_oog();
45            return $ret;
46        }
47    }};
48}
49
50/// Records a `gas` cost and fails the instruction if it would exceed the available gas.
51#[macro_export]
52#[collapse_debuginfo(yes)]
53macro_rules! gas {
54    ($interpreter:expr, $gas:expr) => {
55        $crate::gas!($interpreter, $gas, ())
56    };
57    ($interpreter:expr, $gas:expr, $ret:expr) => {
58        if !$interpreter.gas.record_regular_cost($gas) {
59            $interpreter.halt_oog();
60            return $ret;
61        }
62    };
63}
64
65/// Resizes the interpreter memory if necessary. Fails the instruction if the memory or gas limit
66/// is exceeded.
67#[macro_export]
68#[collapse_debuginfo(yes)]
69macro_rules! resize_memory {
70    ($interpreter:expr, $gas_params:expr, $offset:expr, $len:expr) => {
71        $crate::resize_memory!($interpreter, $gas_params, $offset, $len, ())
72    };
73    ($interpreter:expr, $gas_params:expr, $offset:expr, $len:expr, $ret:expr) => {
74        if let Err(result) = $crate::interpreter::resize_memory(
75            &mut $interpreter.gas,
76            &mut $interpreter.memory,
77            $gas_params,
78            $offset,
79            $len,
80        ) {
81            $interpreter.halt(result);
82            return $ret;
83        }
84    };
85}
86
87/// Pops n values from the stack. Fails the instruction if n values can't be popped.
88#[macro_export]
89#[collapse_debuginfo(yes)]
90macro_rules! popn {
91    ([ $($x:ident),* ],$interpreter:expr $(,$ret:expr)? ) => {
92        let Some([$( $x ),*]) = $interpreter.stack.popn() else {
93            $interpreter.halt_underflow();
94            return $($ret)?;
95        };
96    };
97}
98
99#[doc(hidden)]
100#[macro_export]
101#[collapse_debuginfo(yes)]
102macro_rules! _count {
103    (@count) => { 0 };
104    (@count $head:tt $($tail:tt)*) => { 1 + _count!(@count $($tail)*) };
105    ($($arg:tt)*) => { _count!(@count $($arg)*) };
106}
107
108/// Pops n values from the stack and returns the top value. Fails the instruction if n values can't be popped.
109#[macro_export]
110#[collapse_debuginfo(yes)]
111macro_rules! popn_top {
112    ([ $($x:ident),* ], $top:ident, $interpreter:expr $(,$ret:expr)? ) => {
113        /*
114        let Some(([$( $x ),*], $top)) = $interpreter.stack.popn_top() else {
115            $interpreter.halt($crate::InstructionResult::StackUnderflow);
116            return $($ret)?;
117        };
118        */
119
120        // Workaround for https://github.com/rust-lang/rust/issues/144329.
121        if $interpreter.stack.len() < (1 + $crate::_count!($($x)*)) {
122            $interpreter.halt_underflow();
123            return $($ret)?;
124        }
125        let ([$( $x ),*], $top) = unsafe { $interpreter.stack.popn_top().unwrap_unchecked() };
126    };
127}
128
129/// Pushes a `B256` value onto the stack. Fails the instruction if the stack is full.
130#[macro_export]
131#[collapse_debuginfo(yes)]
132macro_rules! push {
133    ($interpreter:expr, $x:expr $(,$ret:item)?) => (
134        if !($interpreter.stack.push($x)) {
135            $interpreter.halt_overflow();
136            return $($ret)?;
137        }
138    )
139}
140
141/// Converts a `U256` value to a `u64`, saturating to `MAX` if the value is too large.
142#[macro_export]
143#[collapse_debuginfo(yes)]
144macro_rules! as_u64_saturated {
145    ($v:expr) => {
146        u64::try_from($v).unwrap_or(u64::MAX)
147    };
148}
149
150/// Converts a `U256` value to a `usize`, saturating to `MAX` if the value is too large.
151#[macro_export]
152#[collapse_debuginfo(yes)]
153macro_rules! as_usize_saturated {
154    ($v:expr) => {
155        usize::try_from($v).unwrap_or(usize::MAX)
156    };
157}
158
159/// Converts a `U256` value to a `isize`, saturating to `isize::MAX` if the value is too large.
160#[macro_export]
161#[collapse_debuginfo(yes)]
162macro_rules! as_isize_saturated {
163    ($v:expr) => {
164        isize::try_from($v).unwrap_or(isize::MAX)
165    };
166}
167
168/// Converts a `U256` value to a `usize`, failing the instruction if the value is too large.
169#[macro_export]
170#[collapse_debuginfo(yes)]
171macro_rules! as_usize_or_fail {
172    ($interpreter:expr, $v:expr) => {
173        $crate::as_usize_or_fail_ret!($interpreter, $v, ())
174    };
175    ($interpreter:expr, $v:expr, $reason:expr) => {
176        $crate::as_usize_or_fail_ret!($interpreter, $v, $reason, ())
177    };
178}
179
180/// Converts a `U256` value to a `usize` and returns `ret`,
181/// failing the instruction if the value is too large.
182#[macro_export]
183#[collapse_debuginfo(yes)]
184macro_rules! as_usize_or_fail_ret {
185    ($interpreter:expr, $v:expr, $ret:expr) => {
186        $crate::as_usize_or_fail_ret!(
187            $interpreter,
188            $v,
189            $crate::InstructionResult::InvalidOperandOOG,
190            $ret
191        )
192    };
193
194    ($interpreter:expr, $v:expr, $reason:expr, $ret:expr) => {
195        match $v.as_limbs() {
196            x => {
197                if (x[0] > usize::MAX as u64) | (x[1] != 0) | (x[2] != 0) | (x[3] != 0) {
198                    $interpreter.halt($reason);
199                    return $ret;
200                }
201                x[0] as usize
202            }
203        }
204    };
205}