Skip to content

Commit bf1cced

Browse files
authored
Merge pull request #1 from clabby/cl/evm_asm_macro
✨ Add `evm_asm_vec` & `evm_asm` macros
2 parents 7bfedcd + ab9538f commit bf1cced

File tree

3 files changed

+154
-14
lines changed

3 files changed

+154
-14
lines changed

README.md

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ fn main() {
3535

3636
let empty_revert = runtime_marks.next();
3737

38-
let runtime = vec![
38+
let mut runtime = vec![
3939
// Load x, y
4040
Op(PUSH0),
4141
Op(CALLDATALOAD), // x
@@ -63,13 +63,13 @@ fn main() {
6363
Op(REVERT),
6464
];
6565

66-
let runtime_bytecode = assemble_full(&runtime, true).unwrap();
66+
let runtime_bytecode = assemble_full(&mut runtime, true).unwrap();
6767

6868
let mut deploy_marks = MarkTracker::new();
6969
let runtime_start = deploy_marks.next();
7070
let runtime_end = deploy_marks.next();
7171

72-
let deploy = vec![
72+
let mut deploy = vec![
7373
// Constructor
7474
Asm::delta_ref(runtime_start, runtime_end), // rt_size
7575
Op(DUP1), // rt_size, rt_size
@@ -86,7 +86,7 @@ fn main() {
8686
Asm::padded_back(32, vec![data!("49203c3320796f75203a29")]),
8787
];
8888

89-
let deploy_bytecode = assemble_full(&deploy, true).unwrap();
89+
let deploy_bytecode = assemble_full(&mut deploy, true).unwrap();
9090

9191
println!("runtime bytecode: {}", hex::encode(runtime_bytecode));
9292
println!("deploy bytecode: {}", hex::encode(deploy_bytecode));
@@ -129,7 +129,7 @@ let asm = vec![
129129
Op(CALLDATALOAD),
130130
Op(DUP2),
131131
Op(PUSH5(hex!("0000138302"))),
132-
]
132+
];
133133
```
134134

135135
### Marks & References
@@ -198,14 +198,17 @@ tables.
198198
These also have helpers:
199199

200200
```rust
201-
use evm_glue::assembly::Asm;
201+
use evm_glue::{assembly::Asm, utils::MarkTracker};
202202

203+
let mut deploy_marks = MarkTracker::new();
204+
let runtime_start = deploy_marks.next();
205+
let runtime_end = deploy_marks.next();
203206
let asm = vec![
204207
Asm::delta_ref(runtime_start, runtime_end), // Pushed delta reference
205208
// ...,
206209
Asm::mref(runtime_start), // Pushed direct reference
207210
// ...,
208-
Asm::mark(runtime_start),
211+
Asm::Mark(runtime_start),
209212
];
210213
```
211214

@@ -221,7 +224,7 @@ To insert a byte stream (`Vec<u8>`) as data you can use the `Asm::Data` enum mem
221224
```rust
222225
use evm_glue::assembly::Asm::*;
223226

224-
let runtime_bytecode: Vec<u8> = /* some bytecode */;
227+
let runtime_bytecode: Vec<u8> = vec![0x00];
225228

226229
let asm = vec![
227230
Data(runtime_bytecode.clone())
@@ -233,7 +236,7 @@ let asm = vec![
233236
You can use the `evm_glue::data` macro to directly specify a hex literal as data:
234237

235238
```rust
236-
use evm_glue::data;
239+
use evm_glue::{data, assembly::Asm};
237240

238241
let asm = vec![
239242
data!("0283")

src/assembly.rs

Lines changed: 138 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use crate::utils::debug_as_display;
33
use hex;
44
use std::fmt;
55

6-
#[derive(Clone, Debug)]
6+
#[derive(Clone, Debug, PartialEq, Eq)]
77
pub 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)]
2626
pub 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)]
4141
pub enum PadSide {
4242
Front,
4343
Back,
4444
}
4545

46-
#[derive(Clone, Debug)]
46+
#[derive(Clone, Debug, PartialEq, Eq)]
4747
pub 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)]
5454
pub 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+
}

src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1+
#![doc = include_str!("../README.md")]
2+
13
pub mod assembler;
4+
pub use assembler::{assemble_full, validate_asm, AssembleError, AssembleResult};
5+
26
pub mod assembly;
37
pub mod opcodes;
48
pub mod utils;

0 commit comments

Comments
 (0)