@@ -3,7 +3,7 @@ use crate::utils::debug_as_display;
33use hex;
44use std:: fmt;
55
6- #[ derive( Clone , Debug ) ]
6+ #[ derive( Clone , Debug , PartialEq , Eq ) ]
77pub enum RefRepr {
88 Pushed ,
99 Literal ,
@@ -22,7 +22,7 @@ impl RefRepr {
2222 }
2323}
2424
25- #[ derive( Clone , Debug ) ]
25+ #[ derive( Clone , Debug , PartialEq , Eq ) ]
2626pub enum RefType {
2727 Direct ( usize ) ,
2828 Delta ( usize , usize ) ,
@@ -37,20 +37,20 @@ impl fmt::Display for RefType {
3737 }
3838}
3939
40- #[ derive( Clone , Debug , PartialEq ) ]
40+ #[ derive( Clone , Debug , PartialEq , Eq ) ]
4141pub enum PadSide {
4242 Front ,
4343 Back ,
4444}
4545
46- #[ derive( Clone , Debug ) ]
46+ #[ derive( Clone , Debug , PartialEq , Eq ) ]
4747pub struct MarkRef {
4848 pub size : Option < usize > ,
4949 pub ref_type : RefType ,
5050 pub ref_repr : RefRepr ,
5151}
5252
53- #[ derive( Clone , Debug ) ]
53+ #[ derive( Clone , Debug , PartialEq , Eq ) ]
5454pub enum Asm {
5555 Op ( Opcode ) ,
5656 Data ( Vec < u8 > ) ,
@@ -203,3 +203,136 @@ macro_rules! data {
203203 Asm :: Data ( hex!( $bytes) . into( ) )
204204 } } ;
205205}
206+
207+ /// A shorthand for the [evm_asm_vec] macro, see its documentation for more details.
208+ #[ macro_export]
209+ macro_rules! evm_asm {
210+ // Capture everything into a tuple and forward it to the internal handler
211+ ( $( $tt: tt) * ) => { {
212+ let mut result = Vec :: new( ) ;
213+ evm_asm_vec!( result; $( $tt) * ) ;
214+ result
215+ } } ;
216+ }
217+
218+ /// A macro for filling a [Vec] with EVM [Asm] instruction variants.
219+ ///
220+ /// Rules:
221+ /// - Shorthands for all [Asm] variants are supported directly. In order of precedence:
222+ /// - `Mark($expr)` -> `Asm::Mark($expr)`
223+ /// - `Data($expr)` -> `Asm::Data($expr)`
224+ /// - `PaddedBlock { $a:expr, $b:expr, $c:expr, $d: expr }` -> `Asm::PaddedBlock { size: $a, padding: $b, blocks: $c, side: $d }`
225+ /// - `Ref($expr)` -> `Asm::Ref($expr)`
226+ /// - `Op($expr)` -> `Asm::Op($expr)`
227+ /// - `Asm::$variant($($expr:expr),*)` -> `Asm::$variant($($expr),*)`
228+ /// - `VAR $var:ident` -> `$var`
229+ /// - `$head:expr` -> `Asm::Op($head)`
230+ ///
231+ /// **Example**
232+ /// ```rust
233+ /// use evm_glue::{assembly::{*, Asm::*}, opcodes::{*, Opcode::*}, utils::*, evm_asm, evm_asm_vec};
234+ /// use hex_literal::hex;
235+ ///
236+ /// let mut runtime_marks = MarkTracker::new();
237+ /// let empty_revert = runtime_marks.next();
238+ ///
239+ /// let push0_var = Asm::Op(PUSH0);
240+ /// let runtime = vec![
241+ /// // Load x, y
242+ /// push0_var.clone(),
243+ /// Asm::Op(CALLDATALOAD), // x
244+ /// Asm::Op(PUSH1(hex!("20"))),
245+ /// Asm::Op(CALLDATALOAD), // x, y
246+ /// // Add and check for overflow
247+ /// Asm::Op(DUP2), // x, y, x
248+ /// Asm::Op(ADD), // x, r
249+ /// Asm::Op(DUP1), // x, r, r
250+ /// Asm::Op(SWAP2), // r, r, x
251+ /// Asm::Op(GT), // r, x > r
252+ /// Asm::mref(empty_revert), // r, x > r, l
253+ /// Asm::Op(JUMPI), // r
254+ /// // Return result.
255+ /// Asm::Op(MSIZE),
256+ /// Asm::Op(MSTORE),
257+ /// Asm::Op(MSIZE),
258+ /// Asm::Op(PUSH0),
259+ /// Asm::Op(RETURN),
260+ /// // Revert
261+ /// Asm::Mark(empty_revert),
262+ /// Asm::Op(JUMPDEST),
263+ /// Asm::Op(PUSH0),
264+ /// Asm::Op(PUSH0),
265+ /// Asm::Op(REVERT),
266+ /// ];
267+ /// let runtime_macro = evm_asm!(
268+ /// // Load x, y
269+ /// VAR push0_var,
270+ /// CALLDATALOAD, // x
271+ /// PUSH1(hex!("20")),
272+ /// CALLDATALOAD, // x, y
273+ /// // Add and check for overflow
274+ /// DUP2, // x, y, x
275+ /// ADD, // x, r
276+ /// DUP1, // x, r, r
277+ /// SWAP2, // r, r, x
278+ /// GT, // r, x > r
279+ /// Asm::mref(empty_revert), // r, x > r, l
280+ /// JUMPI, // r
281+ /// // Return result.
282+ /// MSIZE,
283+ /// MSTORE,
284+ /// MSIZE,
285+ /// PUSH0,
286+ /// RETURN,
287+ /// // Revert
288+ /// Mark(empty_revert),
289+ /// JUMPDEST,
290+ /// PUSH0,
291+ /// PUSH0,
292+ /// REVERT,
293+ /// );
294+ /// assert_eq!(runtime_macro, runtime);
295+ /// ```
296+ #[ macro_export]
297+ macro_rules! evm_asm_vec {
298+ // Non-opcode variant matchers take precedent
299+ ( $res: ident; Mark ( $expr: expr) $( , $( $rest: tt) * ) ?) => {
300+ $res. push( Asm :: Mark ( $expr) ) ;
301+ evm_asm_vec!( $res; $( $( $rest) * ) ?) ;
302+ } ;
303+ ( $res: ident; Data ( $lit: literal) $( , $( $rest: tt) * ) ?) => {
304+ $res. push( data!( $lit) ) ;
305+ evm_asm_vec!( $res; $( $( $rest) * ) ?) ;
306+ } ;
307+ ( $res: ident; PaddedBlock { $a: expr, $b: expr, $c: expr, $d: expr } $( , $( $rest: tt) * ) ?) => {
308+ $res. push( Asm :: PaddedBlock { size: $a, padding: $b, blocks: $c, side: $d } ) ;
309+ evm_asm_vec!( $res; $( $( $rest) * ) ?) ;
310+ } ;
311+ ( $res: ident; Ref ( $expr: expr) $( , $( $rest: tt) * ) ?) => {
312+ $res. push( Ref ( $expr) ) ;
313+ evm_asm_vec!( $res; $( $( $rest) * ) ?) ;
314+ } ;
315+ ( $res: ident; Op ( $expr: expr) $( , $( $rest: tt) * ) ?) => {
316+ $res. push( Op ( $expr) ) ;
317+ evm_asm_vec!( $res; $( $( $rest) * ) ?) ;
318+ } ;
319+ // Match any qualified Asm variants
320+ ( $res: ident; Asm :: $variant: ident( $( $expr: expr) ,* ) $( , $( $rest: tt) * ) ?) => {
321+ $res. push( Asm :: $variant( $( $expr) ,* ) ) ;
322+ evm_asm_vec!( $res; $( $( $rest) * ) ?) ;
323+ } ;
324+ // Allow for passing in var idents.
325+ ( $res: ident; VAR $var: ident $( , $( $rest: tt) * ) ?) => {
326+ $res. push( $var) ;
327+ evm_asm_vec!( $res; $( $( $rest) * ) ?) ;
328+ } ;
329+ // Assume anything else is an opcode
330+ ( $res: ident; $head: expr $( , $( $rest: tt) * ) ?) => {
331+ // TODO: Could further pattern match to allow for ambiguous functions / other types of
332+ // exprs.
333+ $res. push( Op ( $head) ) ;
334+ evm_asm_vec!( $res; $( $( $rest) * ) ?) ;
335+ } ;
336+ // Terminal case: all tokens have been consumed
337+ ( $res: ident; ) => { } ;
338+ }
0 commit comments