From e723a65cefdc5a8ac58f99123769e8f84a1253ce Mon Sep 17 00:00:00 2001 From: Gaston Zanitti Date: Tue, 12 Sep 2023 15:41:08 -0300 Subject: [PATCH 01/73] WIP --- etk-asm/src/asm.rs | 66 ++++++++++++++++++++++++++-------------------- 1 file changed, 38 insertions(+), 28 deletions(-) diff --git a/etk-asm/src/asm.rs b/etk-asm/src/asm.rs index a0555961..f7cefaa9 100644 --- a/etk-asm/src/asm.rs +++ b/etk-asm/src/asm.rs @@ -198,20 +198,13 @@ impl From> for RawOp { /// # assert_eq!(output, hex!("58")); /// # Result::<(), Error>::Ok(()) /// ``` -#[derive(Debug)] + pub struct Assembler { /// Assembled ops, ready to be taken. ready: Vec, - /// Ops that cannot be encoded yet. - pending: VecDeque, - - /// Sum of the size of all the ops in `pending`, or `None` if `pending` contains - /// an unsized op. - pending_len: Option, - - /// Total number of `u8` that have been appended to `ready`. - concrete_len: usize, + /// Assembled ops, not yet ready to be taken. + unrolled: Vec, /// Labels, in `pending`, associated with an `AbstractOp::Label`. declared_labels: HashMap>, @@ -221,19 +214,32 @@ pub struct Assembler { /// Labels, in `pending`, that have been referred to (ex. with push) but /// have not been declared with an `AbstractOp::Label`. - undeclared_labels: HashSet, + undeclared_labels: Vec, + + /// Macros in pending + undeclared_macros: Vec, +} +#[derive(Debug)] +struct PendingMacro { + position: usize, + name: String, + arguments: Vec, +} +#[derive(Debug)] +struct PendingLabel { + position: usize, + name: String, } impl Default for Assembler { fn default() -> Self { Self { ready: Default::default(), - pending: Default::default(), - pending_len: Some(0), - concrete_len: 0, + unrolled: Default::default(), declared_labels: Default::default(), declared_macros: Default::default(), undeclared_labels: Default::default(), + undeclared_macros: Default::default(), } } } @@ -311,8 +317,7 @@ impl Assembler { Ok(self.ready.len()) } - /// Insert explicilty declared macros and labels, via `AbstractOp`, and implictly declared - /// macros and labels via usage in `Op`. + /// Insert explicilty declared macros and labels, via `AbstractOp`. fn declare_content(&mut self, rop: &RawOp) -> Result<(), Error> { match rop { RawOp::Op(AbstractOp::Label(ref label)) => { @@ -321,8 +326,15 @@ impl Assembler { return error::DuplicateLabel { label }.fail(); } hash_map::Entry::Vacant(v) => { - v.insert(None); - self.undeclared_labels.remove(label); + v.insert(Some(self.ready.len())); + + for ul in self.undeclared_labels.iter() { + if ul.name == *label { + self.unrolled.insert(ul.position, rop.clone()); + } + } + + self.undeclared_labels.retain(|ul| ul.name != *label); } } } @@ -333,22 +345,20 @@ impl Assembler { } hash_map::Entry::Vacant(v) => { v.insert(defn.to_owned()); + + for um in self.undeclared_macros.iter() { + if um.name == *defn.name() { + self.unrolled.insert(um.position, rop.clone()); + } + } + + self.undeclared_macros.retain(|um| um.name != *defn.name()); } } } _ => (), }; - // Get all labels used by `rop`, check if they've been defined, and if not, note them as - // "undeclared". - if let Some(Ok(labels)) = rop.expr().map(|e| e.labels(&self.declared_macros)) { - for label in labels { - if !self.declared_labels.contains_key(&label) { - self.undeclared_labels.insert(label.to_owned()); - } - } - } - Ok(()) } From d8da0527e949af1bbf6719468a6dc5a5c0cad279 Mon Sep 17 00:00:00 2001 From: Gaston Zanitti Date: Tue, 12 Sep 2023 19:29:36 -0300 Subject: [PATCH 02/73] Trying some ideas --- etk-asm/src/asm.rs | 57 +++++++++++++--------------------------------- 1 file changed, 16 insertions(+), 41 deletions(-) diff --git a/etk-asm/src/asm.rs b/etk-asm/src/asm.rs index f7cefaa9..fc904747 100644 --- a/etk-asm/src/asm.rs +++ b/etk-asm/src/asm.rs @@ -198,14 +198,11 @@ impl From> for RawOp { /// # assert_eq!(output, hex!("58")); /// # Result::<(), Error>::Ok(()) /// ``` - +#[derive(Debug)] pub struct Assembler { /// Assembled ops, ready to be taken. ready: Vec, - /// Assembled ops, not yet ready to be taken. - unrolled: Vec, - /// Labels, in `pending`, associated with an `AbstractOp::Label`. declared_labels: HashMap>, @@ -214,32 +211,16 @@ pub struct Assembler { /// Labels, in `pending`, that have been referred to (ex. with push) but /// have not been declared with an `AbstractOp::Label`. - undeclared_labels: Vec, - - /// Macros in pending - undeclared_macros: Vec, -} -#[derive(Debug)] -struct PendingMacro { - position: usize, - name: String, - arguments: Vec, -} -#[derive(Debug)] -struct PendingLabel { - position: usize, - name: String, + undeclared_labels: HashSet, } impl Default for Assembler { fn default() -> Self { Self { ready: Default::default(), - unrolled: Default::default(), declared_labels: Default::default(), declared_macros: Default::default(), undeclared_labels: Default::default(), - undeclared_macros: Default::default(), } } } @@ -317,7 +298,8 @@ impl Assembler { Ok(self.ready.len()) } - /// Insert explicilty declared macros and labels, via `AbstractOp`. + /// Insert explicilty declared macros and labels, via `AbstractOp`, and implictly declared + /// macros and labels via usage in `Op`. fn declare_content(&mut self, rop: &RawOp) -> Result<(), Error> { match rop { RawOp::Op(AbstractOp::Label(ref label)) => { @@ -326,15 +308,8 @@ impl Assembler { return error::DuplicateLabel { label }.fail(); } hash_map::Entry::Vacant(v) => { - v.insert(Some(self.ready.len())); - - for ul in self.undeclared_labels.iter() { - if ul.name == *label { - self.unrolled.insert(ul.position, rop.clone()); - } - } - - self.undeclared_labels.retain(|ul| ul.name != *label); + v.insert(None); + self.undeclared_labels.remove(label); } } } @@ -345,20 +320,22 @@ impl Assembler { } hash_map::Entry::Vacant(v) => { v.insert(defn.to_owned()); - - for um in self.undeclared_macros.iter() { - if um.name == *defn.name() { - self.unrolled.insert(um.position, rop.clone()); - } - } - - self.undeclared_macros.retain(|um| um.name != *defn.name()); } } } _ => (), }; + // Get all labels used by `rop`, check if they've been defined, and if not, note them as + // "undeclared". + if let Some(Ok(labels)) = rop.expr().map(|e| e.labels(&self.declared_macros)) { + for label in labels { + if !self.declared_labels.contains_key(&label) { + self.undeclared_labels.insert(label.to_owned()); + } + } + } + Ok(()) } @@ -699,8 +676,6 @@ impl Assembler { Ok(Some(self.push_all(m.contents)?)) } _ => { - assert_eq!(self.pending_len, Some(0)); - self.pending_len = None; self.pending.push_back(RawOp::Op(AbstractOp::Macro( ops::InstructionMacroInvocation { name: name.to_string(), From 027154c807e28356900b77de3893cc54ab20a35e Mon Sep 17 00:00:00 2001 From: Gaston Zanitti Date: Wed, 13 Sep 2023 16:32:34 -0300 Subject: [PATCH 03/73] Still WIP. Need to call finish() at the end. --- etk-asm/src/asm.rs | 394 +++++++++------------------- etk-asm/tests/asm.rs | 30 +++ etk-asm/tests/asm/every-op/test.etk | 7 + 3 files changed, 157 insertions(+), 274 deletions(-) create mode 100644 etk-asm/tests/asm/every-op/test.etk diff --git a/etk-asm/src/asm.rs b/etk-asm/src/asm.rs index fc904747..49e93428 100644 --- a/etk-asm/src/asm.rs +++ b/etk-asm/src/asm.rs @@ -130,9 +130,10 @@ mod error { } pub use self::error::Error; +use crate::ops::expression::Error::{UndefinedVariable, UnknownLabel, UnknownMacro}; use crate::ops::expression::{self, Terminal}; use crate::ops::{self, AbstractOp, Assemble, Expression, Imm, MacroDefinition}; -use etk_ops::cancun::Op; +use etk_ops::cancun::{Op, Push1}; use rand::Rng; use snafu::OptionExt; use std::collections::{hash_map, HashMap, HashSet, VecDeque}; @@ -201,7 +202,10 @@ impl From> for RawOp { #[derive(Debug)] pub struct Assembler { /// Assembled ops, ready to be taken. - ready: Vec, + ready: Vec, + + /// Number ob bytes in 'ready' that are ready to be taken. + concrete_len: usize, /// Labels, in `pending`, associated with an `AbstractOp::Label`. declared_labels: HashMap>, @@ -211,16 +215,42 @@ pub struct Assembler { /// Labels, in `pending`, that have been referred to (ex. with push) but /// have not been declared with an `AbstractOp::Label`. - undeclared_labels: HashSet, + undefined_labels: Vec, + + /// Macros, in `pending`, that have been referred to but + /// have not been declared with an `AbstractOp::Macro`. + undefined_macros: Vec, +} + +/// TODO Temporal DOC +#[derive(Debug)] +pub struct PendingLabel { + /// The name of the label. + label: String, + + /// The offset of the label. + offset: usize, +} + +/// TODO Temporal DOC +#[derive(Debug)] +pub struct PendingMacro { + /// The name of the macro. + name: String, + + /// The offset of the macro. + offset: usize, } impl Default for Assembler { fn default() -> Self { Self { ready: Default::default(), + concrete_len: 0, declared_labels: Default::default(), declared_macros: Default::default(), - undeclared_labels: Default::default(), + undefined_labels: Default::default(), + undefined_macros: Default::default(), } } } @@ -231,56 +261,53 @@ impl Assembler { Self::default() } - /// Indicate that the input sequence is complete. Returns any errors that - /// may remain. - pub fn finish(self) -> Result<(), Error> { - if let Some(undef) = self.pending.front() { - return match undef { - RawOp::Op(AbstractOp::Macro(invc)) => error::UndeclaredInstructionMacro { - name: invc.name.clone(), - } - .fail(), - RawOp::Op(op) => { - match op - .clone() - .concretize((&self.declared_labels, &self.declared_macros).into()) - { - Ok(_) => unreachable!(), - Err(ops::Error::ContextIncomplete { - source: expression::Error::UnknownMacro { name, .. }, - .. - }) => error::UndeclaredExpressionMacro { name }.fail(), - Err(ops::Error::ContextIncomplete { - source: expression::Error::UnknownLabel { .. }, - .. - }) => { - let labels = op.expr().unwrap().labels(&self.declared_macros).unwrap(); - let declared = self.declared_labels.into_keys().collect(); - let invoked: HashSet<_> = labels.into_iter().collect(); - let missing = invoked - .difference(&declared) - .cloned() - .collect::>(); - error::UndeclaredLabels { labels: missing }.fail() - } - _ => unreachable!(), - } - } - // bug: if a variable is used when it isn't available, e.g. push1 $size - _ => unreachable!(), - }; + /// Collect any assembled instructions that are ready to be output. + pub fn take(&mut self) -> Vec { + let output = self.finish(); + match output { + Ok(v) => std::mem::take(&mut v.clone()), + Err(e) => { + eprintln!("error: {}", e); + Vec::new() + } } + } - if !self.ready.is_empty() { - panic!("not all assembled bytecode has been taken"); + /// Temporal documentation. TODO + pub fn finish(&mut self) -> Result, Error> { + if !self.undefined_labels.is_empty() { + return error::UndeclaredLabels { + labels: self + .undefined_labels + .iter() + .map(|l| l.label.to_owned()) + .collect::>(), + } + .fail(); } - Ok(()) - } + if !self.undefined_macros.is_empty() { + return error::UndeclaredInstructionMacro { + name: self.undefined_macros[0].name.to_owned(), + } + .fail(); + } - /// Collect any assembled instructions that are ready to be output. - pub fn take(&mut self) -> Vec { - std::mem::take(&mut self.ready) + // Concretize every RawOp ready, and collect the results. + let mut output = Vec::new(); + for ready_op in self.ready.iter() { + if let &RawOp::Op(ref op) = ready_op { + match op + .clone() + .concretize((&self.declared_labels, &self.declared_macros).into()) + { + Ok(cop) => cop.assemble(&mut output), + Err(e) => unreachable!("all ops should be concretizable: {}", e), + } + } + } + + Ok(output) } /// Feed instructions into the `Assembler`. @@ -309,7 +336,6 @@ impl Assembler { } hash_map::Entry::Vacant(v) => { v.insert(None); - self.undeclared_labels.remove(label); } } } @@ -320,21 +346,29 @@ impl Assembler { } hash_map::Entry::Vacant(v) => { v.insert(defn.to_owned()); + + for m in self.undefined_macros.iter() { + if m.name == *defn.name() { + self.ready.insert(m.offset, rop.clone()); + } + } } } } - _ => (), + _ => { + println!("declare_content (salio): {:?}", rop) + } }; // Get all labels used by `rop`, check if they've been defined, and if not, note them as // "undeclared". - if let Some(Ok(labels)) = rop.expr().map(|e| e.labels(&self.declared_macros)) { - for label in labels { - if !self.declared_labels.contains_key(&label) { - self.undeclared_labels.insert(label.to_owned()); - } - } - } + //if let Some(Ok(labels)) = rop.expr().map(|e| e.labels(&self.declared_macros)) { + // for label in labels { + // if !self.declared_labels.contains_key(&label) { + // self.undefined_labels.insert(label.to_owned()); + // } + // } + //} Ok(()) } @@ -358,18 +392,10 @@ impl Assembler { return Ok(self.ready.len()); } - self.push_unchecked(rop)?; + self.push_ready(rop)?; Ok(self.ready.len()) } - fn push_unchecked(&mut self, rop: RawOp) -> Result<(), Error> { - if self.pending.is_empty() && self.pending_len.is_some() { - self.push_ready(rop) - } else { - self.push_pending(rop) - } - } - fn push_ready(&mut self, rop: RawOp) -> Result<(), Error> { match rop { RawOp::Op(AbstractOp::Label(label)) => { @@ -381,18 +407,7 @@ impl Assembler { Ok(()) } RawOp::Op(AbstractOp::MacroDefinition(_)) => Ok(()), - RawOp::Op(AbstractOp::Macro(ref m)) => { - match self.declared_macros.get(&m.name) { - // Do nothing if the instruction macro has been defined. - Some(MacroDefinition::Instruction(_)) => (), - _ => { - assert_eq!(self.pending_len, Some(0)); - self.pending_len = None; - self.pending.push_back(rop); - } - } - Ok(()) - } + RawOp::Op(AbstractOp::Macro(ref m)) => Ok(()), RawOp::Op(ref op) => { match op .clone() @@ -400,7 +415,7 @@ impl Assembler { { Ok(cop) => { self.concrete_len += cop.size(); - cop.assemble(&mut self.ready); + self.ready.push(rop); } Err(ops::Error::ExpressionTooLarge { value, spec, .. }) => { return error::ExpressionTooLarge { @@ -417,205 +432,35 @@ impl Assembler { } .fail() } - Err(_) => { - assert_eq!(self.pending_len, Some(0)); - self.pending_len = rop.size(); - self.pending.push_back(rop); - } + Err(ops::Error::ContextIncomplete { source }) => match source { + UnknownLabel { label, .. } => { + self.ready.push(rop); + self.undefined_labels.push(PendingLabel { + label: label.to_owned(), + offset: self.concrete_len, + }); + } + UnknownMacro { name, .. } => { + self.ready.push(rop); + self.undefined_macros.push(PendingMacro { + name: name.to_owned(), + offset: self.concrete_len, + }); + } + UndefinedVariable { name, .. } => todo!(), + }, } Ok(()) } RawOp::Raw(raw) => { self.concrete_len += raw.len(); - self.ready.extend(raw); + self.ready.push(RawOp::Raw(raw)); Ok(()) } } } - fn push_pending(&mut self, rop: RawOp) -> Result<(), Error> { - // Update total size of pending ops. - if let Some(ref mut pending_len) = self.pending_len { - match rop.size() { - Some(size) => *pending_len += size, - None => self.pending_len = None, - } - } - - // Handle new label and macro definitions. - match (self.pending_len, rop) { - (Some(pending_len), RawOp::Op(AbstractOp::Label(lbl))) => { - // The label has a defined address. - let address = self.concrete_len + pending_len; - let item = self.declared_labels.get_mut(&*lbl).unwrap(); - *item = Some(address); - } - (None, rop @ RawOp::Op(AbstractOp::Label(_))) => { - self.pending.push_back(rop); - if self.undeclared_labels.is_empty() { - self.choose_sizes()?; - } - } - (_, RawOp::Op(AbstractOp::MacroDefinition(defn))) => { - if let Some(RawOp::Op(AbstractOp::Macro(invc))) = self.pending.front() { - if defn.name() == &invc.name { - let invc = invc.clone(); - self.pending.pop_front(); - self.expand_macro(&invc.name, &invc.parameters)?; - } - } - } - (_, rop) => { - // Not a label. - self.pending.push_back(rop); - } - } - - // Repeatedly check if the front of the pending list is ready. - while let Some(next) = self.pending.front() { - let op = match next { - RawOp::Op(AbstractOp::Push(Imm { - tree: Expression::Terminal(Terminal::Label(_)), - .. - })) => { - if self.undeclared_labels.is_empty() { - unreachable!() - } else { - // Still waiting on more labels. - break; - } - } - RawOp::Op(AbstractOp::Label(_)) => unreachable!(), - RawOp::Op(AbstractOp::Macro(_)) => { - // Still waiting on more macros. - break; - } - RawOp::Op(op) => op, - RawOp::Raw(_) => { - self.pop_pending()?; - continue; - } - }; - - match op - .clone() - .concretize((&self.declared_labels, &self.declared_macros).into()) - { - Ok(cop) => { - let front = self.pending.front_mut().unwrap(); - *front = RawOp::Op(cop.into()); - } - Err(ops::Error::ExpressionTooLarge { value, spec, .. }) => { - return error::ExpressionTooLarge { - expr: op.expr().unwrap().clone(), - value, - spec, - } - .fail(); - } - Err(_) => { - // Still waiting for some definition. - break; - } - } - - self.pop_pending()?; - } - - Ok(()) - } - - fn pop_pending(&mut self) -> Result<(), Error> { - let popped = self.pending.pop_front().unwrap(); - - let size; - - match popped { - RawOp::Raw(raw) => { - size = raw.len(); - self.ready.extend(raw); - } - RawOp::Op(aop) => { - let cop = aop - .concretize((&self.declared_labels, &self.declared_macros).into()) - // Already able to concretize in `push_pending` loop. - .unwrap(); - size = cop.size(); - cop.assemble(&mut self.ready); - } - } - - self.concrete_len += size; - - if self.pending.is_empty() { - self.pending_len = Some(0); - } else if let Some(ref mut pending_len) = self.pending_len { - *pending_len -= size; - } - - Ok(()) - } - - fn choose_sizes(&mut self) -> Result<(), Error> { - let mut sizes: HashMap> = self - .pending - .iter() - .filter(|op| matches!(op, RawOp::Op(AbstractOp::Push(_)))) - .map(|op| (op.expr().unwrap().clone(), Op::<()>::push_for(1).unwrap())) - .collect(); - - let mut subasm; - - loop { - // Create a sub-assembler to try assembling with the sizes in - // `undefined_labels`. - subasm = Self::default(); - subasm.concrete_len = self.concrete_len; - subasm.declared_labels = self.declared_labels.clone(); - - let result: Result, Error> = self - .pending - .iter() - .map(|op| { - let new = match op { - RawOp::Op(AbstractOp::Push(Imm { tree, .. })) => { - let new = sizes[tree].with(tree.clone()).unwrap(); - let aop = AbstractOp::new(new); - RawOp::Op(aop) - } - op => op.clone(), - }; - - subasm.push_pending(new) - }) - .collect(); - - match result { - Ok(_) => { - assert!(subasm.pending.is_empty()); - break; - } - Err(Error::ExpressionTooLarge { expr, .. }) => { - // If an expression is too large for an op, increase the width of that op. - let item = sizes.get_mut(&expr).unwrap(); - let new_size = item.upsize().context(error::UnsizedPushTooLarge)?; - *item = new_size; - } - Err(e) => return Err(e), - } - } - - // Insert the results of the sub-assembler into self. - let raw = subasm.take(); - self.pending_len = Some(raw.len()); - self.pending.clear(); - self.pending.push_back(RawOp::Raw(raw)); - self.declared_labels = subasm.declared_labels; - - Ok(()) - } - fn expand_macro( &mut self, name: &str, @@ -676,18 +521,17 @@ impl Assembler { Ok(Some(self.push_all(m.contents)?)) } _ => { - self.pending.push_back(RawOp::Op(AbstractOp::Macro( - ops::InstructionMacroInvocation { - name: name.to_string(), - parameters: parameters.to_vec(), - }, - ))); + self.undefined_macros.push(PendingMacro { + name: name.to_owned(), + offset: self.ready.len(), + }); Ok(None) } } } } +/* #[cfg(test)] mod tests { use super::*; @@ -1385,3 +1229,5 @@ mod tests { Ok(()) } } + + */ diff --git a/etk-asm/tests/asm.rs b/etk-asm/tests/asm.rs index 36fc72cf..1a56b71f 100644 --- a/etk-asm/tests/asm.rs +++ b/etk-asm/tests/asm.rs @@ -276,3 +276,33 @@ fn every_op() -> Result<(), Error> { Ok(()) } + +#[test] +fn every_op2() -> Result<(), Error> { + let mut output = Vec::new(); + let mut ingester = Ingest::new(&mut output); + ingester.ingest_file(source(&["every-op", "test.etk"]))?; + + //assert_eq!( + // output, + // hex!( + // " + // 00 + //" + // ) + //); + + // 5b: jumpdest + // 60: push1 + // 56: jump + + //label1: + //jumpdest + // push1 0x01 + + //push1 label1 + //jump + println!("{:x?}", output); + + Ok(()) +} diff --git a/etk-asm/tests/asm/every-op/test.etk b/etk-asm/tests/asm/every-op/test.etk new file mode 100644 index 00000000..df360571 --- /dev/null +++ b/etk-asm/tests/asm/every-op/test.etk @@ -0,0 +1,7 @@ +push1 label1 +jump + +label1: + jumpdest + push1 0x01 + From 27321ce6e256c83bf40ccb0bf5843aa10599147d Mon Sep 17 00:00:00 2001 From: Gaston Zanitti Date: Wed, 13 Sep 2023 17:33:05 -0300 Subject: [PATCH 04/73] Issue with internal labels (macros) --- etk-asm/src/asm.rs | 24 ++++++++++-------- etk-asm/src/ingest.rs | 13 ++++++++++ etk-asm/tests/asm.rs | 7 +++++- etk-asm/tests/asm/every-op/test.etk | 39 +++++++++++++++++++++++++---- 4 files changed, 67 insertions(+), 16 deletions(-) diff --git a/etk-asm/src/asm.rs b/etk-asm/src/asm.rs index 49e93428..16c7037b 100644 --- a/etk-asm/src/asm.rs +++ b/etk-asm/src/asm.rs @@ -336,6 +336,7 @@ impl Assembler { } hash_map::Entry::Vacant(v) => { v.insert(None); + self.undefined_labels.retain(|l| l.label != *label); } } } @@ -355,20 +356,21 @@ impl Assembler { } } } - _ => { - println!("declare_content (salio): {:?}", rop) - } + _ => {} }; // Get all labels used by `rop`, check if they've been defined, and if not, note them as // "undeclared". - //if let Some(Ok(labels)) = rop.expr().map(|e| e.labels(&self.declared_macros)) { - // for label in labels { - // if !self.declared_labels.contains_key(&label) { - // self.undefined_labels.insert(label.to_owned()); - // } - // } - //} + if let Some(Ok(labels)) = rop.expr().map(|e| e.labels(&self.declared_macros)) { + for label in labels { + if !self.declared_labels.contains_key(&label) { + self.undefined_labels.push(PendingLabel { + label: label.to_owned(), + offset: self.ready.len(), + }); + } + } + } Ok(()) } @@ -381,6 +383,7 @@ impl Assembler { O: Into, { let rop = rop.into(); + println!("pushing {:?}", rop); self.declare_content(&rop)?; @@ -484,6 +487,7 @@ impl Assembler { // First pass, find locally defined labels and rename them. for op in m.contents.iter_mut() { + println!("macro {:?} op: {:?}", name, op); match op { AbstractOp::Label(ref mut label) => { let mangled = format!("{}_{}_{}", m.name, label, rng.gen::()); diff --git a/etk-asm/src/ingest.rs b/etk-asm/src/ingest.rs index 05227609..c1c8577a 100644 --- a/etk-asm/src/ingest.rs +++ b/etk-asm/src/ingest.rs @@ -314,6 +314,17 @@ where } } + fn finish(&mut self) { + for frame in self.sources.iter_mut().rev() { + let asm = match frame.scope { + Scope::Same => continue, + Scope::Independent(ref mut a) => a, + }; + + asm.finish().unwrap(); + } + } + fn write(&mut self, mut op: RawOp) -> Result<(), Error> { if self.sources.is_empty() { panic!("no sources!"); @@ -462,6 +473,8 @@ where } } + self.sources.finish(); + if !self.sources.sources.is_empty() { panic!("extra sources?"); } diff --git a/etk-asm/tests/asm.rs b/etk-asm/tests/asm.rs index 1a56b71f..25a0fb75 100644 --- a/etk-asm/tests/asm.rs +++ b/etk-asm/tests/asm.rs @@ -75,6 +75,8 @@ fn instruction_macro() -> Result<(), Error> { let mut ingester = Ingest::new(&mut output); ingester.ingest_file(source(&["instruction-macro", "main.etk"]))?; + println!("{:x?}", output); + assert_eq!( output, hex!("5b5860005660406005600d601561000061004260086100006100426008") @@ -297,12 +299,15 @@ fn every_op2() -> Result<(), Error> { // 56: jump //label1: - //jumpdest + // djumpdest // push1 0x01 //push1 label1 //jump + + // [91, 88, 96, 0, 86, 96, 64, 96, 5, 96, 3|13, 96, 17|21, 97, 0, 0, 97, 0, 66, 96, 8, 97, 0, 0, 97, 0, 66, 96, 8] println!("{:x?}", output); + println!("{:?}", output); Ok(()) } diff --git a/etk-asm/tests/asm/every-op/test.etk b/etk-asm/tests/asm/every-op/test.etk index df360571..1fa0c7b0 100644 --- a/etk-asm/tests/asm/every-op/test.etk +++ b/etk-asm/tests/asm/every-op/test.etk @@ -1,7 +1,36 @@ -push1 label1 -jump +%macro foo() + push1 start + jump +%end -label1: - jumpdest - push1 0x01 +# // [91, 88, 96, 0, 86, 96, 64, 96, 5, 96, 3|13, 96, 17|21, 97, 0, 0, 97, 0, 66, 96, 8, 97, 0, 0, 97, 0, 66, 96, 8] + +%macro bar() + start: + push1 0x40 + push1 start +%end + +%macro foobar(tag, gat) + push2 $tag + push2 $gat + push1 delayed_expr(7) +%end + +%def delayed_expr(x) + $x + 1 +%end + +start: + jumpdest + pc + %foo() + %bar() + push1 delayed + push1 delayed2 + +delayed: + %foobar(start, 0x42) +delayed2: + %foobar(start, 0x42) From 95a2fd4cd5c934489230da50bca5dbeb3e13e873 Mon Sep 17 00:00:00 2001 From: Gaston Zanitti Date: Thu, 14 Sep 2023 15:15:22 -0300 Subject: [PATCH 05/73] Some tests passing. Issue with includes --- etk-asm/src/asm.rs | 9 +++++-- etk-asm/tests/asm.rs | 9 +++++++ etk-asm/tests/asm/every-op/test.etk | 39 +++-------------------------- 3 files changed, 20 insertions(+), 37 deletions(-) diff --git a/etk-asm/src/asm.rs b/etk-asm/src/asm.rs index 16c7037b..2449947a 100644 --- a/etk-asm/src/asm.rs +++ b/etk-asm/src/asm.rs @@ -361,7 +361,7 @@ impl Assembler { // Get all labels used by `rop`, check if they've been defined, and if not, note them as // "undeclared". - if let Some(Ok(labels)) = rop.expr().map(|e| e.labels(&self.declared_macros)) { + /*if let Some(Ok(labels)) = rop.expr().map(|e| e.labels(&self.declared_macros)) { for label in labels { if !self.declared_labels.contains_key(&label) { self.undefined_labels.push(PendingLabel { @@ -370,7 +370,7 @@ impl Assembler { }); } } - } + }*/ Ok(()) } @@ -437,6 +437,11 @@ impl Assembler { } Err(ops::Error::ContextIncomplete { source }) => match source { UnknownLabel { label, .. } => { + let size = match op.size() { + Some(size) => size, + None => 2, // push + label + }; + self.concrete_len += size; self.ready.push(rop); self.undefined_labels.push(PendingLabel { label: label.to_owned(), diff --git a/etk-asm/tests/asm.rs b/etk-asm/tests/asm.rs index 25a0fb75..5487409d 100644 --- a/etk-asm/tests/asm.rs +++ b/etk-asm/tests/asm.rs @@ -308,6 +308,15 @@ fn every_op2() -> Result<(), Error> { // [91, 88, 96, 0, 86, 96, 64, 96, 5, 96, 3|13, 96, 17|21, 97, 0, 0, 97, 0, 66, 96, 8, 97, 0, 0, 97, 0, 66, 96, 8] println!("{:x?}", output); println!("{:?}", output); + println!( + "Expected {:?}", + hex!("5b5860005660406005600d601561000061004260086100006100426008") + ); + + assert_eq!( + output, + hex!("5b5860005660406005600d601561000061004260086100006100426008") + ); Ok(()) } diff --git a/etk-asm/tests/asm/every-op/test.etk b/etk-asm/tests/asm/every-op/test.etk index 1fa0c7b0..e1df3285 100644 --- a/etk-asm/tests/asm/every-op/test.etk +++ b/etk-asm/tests/asm/every-op/test.etk @@ -1,36 +1,5 @@ -%macro foo() - push1 start - jump -%end - -# // [91, 88, 96, 0, 86, 96, 64, 96, 5, 96, 3|13, 96, 17|21, 97, 0, 0, 97, 0, 66, 96, 8, 97, 0, 0, 97, 0, 66, 96, 8] - -%macro bar() - start: - push1 0x40 - push1 start -%end - -%macro foobar(tag, gat) - push2 $tag - push2 $gat - push1 delayed_expr(7) -%end - -%def delayed_expr(x) - $x + 1 -%end - -start: - jumpdest - pc - %foo() - %bar() - push1 delayed - push1 delayed2 - -delayed: - %foobar(start, 0x42) -delayed2: - %foobar(start, 0x42) +%push(hello) +jump +hello: +jumpdest \ No newline at end of file From ea5a59073c71eead4614660bb9ca065512f461a8 Mon Sep 17 00:00:00 2001 From: Gaston Zanitti Date: Fri, 15 Sep 2023 12:09:08 -0300 Subject: [PATCH 06/73] All ASM test passing --- etk-asm/src/asm.rs | 15 +++++++++++---- etk-asm/src/ingest.rs | 8 ++++---- etk-asm/tests/asm.rs | 19 ++++++------------- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/etk-asm/src/asm.rs b/etk-asm/src/asm.rs index 2449947a..d39aae3a 100644 --- a/etk-asm/src/asm.rs +++ b/etk-asm/src/asm.rs @@ -265,7 +265,11 @@ impl Assembler { pub fn take(&mut self) -> Vec { let output = self.finish(); match output { - Ok(v) => std::mem::take(&mut v.clone()), + Ok(v) => { + self.ready.clear(); + self.concrete_len = 0; + v + } Err(e) => { eprintln!("error: {}", e); Vec::new() @@ -296,7 +300,7 @@ impl Assembler { // Concretize every RawOp ready, and collect the results. let mut output = Vec::new(); for ready_op in self.ready.iter() { - if let &RawOp::Op(ref op) = ready_op { + if let RawOp::Op(ref op) = ready_op { match op .clone() .concretize((&self.declared_labels, &self.declared_macros).into()) @@ -304,6 +308,9 @@ impl Assembler { Ok(cop) => cop.assemble(&mut output), Err(e) => unreachable!("all ops should be concretizable: {}", e), } + } else if let RawOp::Raw(raw) = ready_op { + self.concrete_len += raw.len(); + output.extend(raw); } } @@ -392,11 +399,11 @@ impl Assembler { // individually which calls the correct unchecked push. if let RawOp::Op(AbstractOp::Macro(ref m)) = rop { self.expand_macro(&m.name, &m.parameters)?; - return Ok(self.ready.len()); + return Ok(self.concrete_len); } self.push_ready(rop)?; - Ok(self.ready.len()) + Ok(self.concrete_len) } fn push_ready(&mut self, rop: RawOp) -> Result<(), Error> { diff --git a/etk-asm/src/ingest.rs b/etk-asm/src/ingest.rs index c1c8577a..9bc91adc 100644 --- a/etk-asm/src/ingest.rs +++ b/etk-asm/src/ingest.rs @@ -297,7 +297,7 @@ where }; let raw = asm.take(); - asm.finish()?; + //asm.finish()?; if raw.is_empty() { return Ok(()); @@ -314,7 +314,7 @@ where } } - fn finish(&mut self) { + /*fn finish(&mut self) { for frame in self.sources.iter_mut().rev() { let asm = match frame.scope { Scope::Same => continue, @@ -323,7 +323,7 @@ where asm.finish().unwrap(); } - } + }*/ fn write(&mut self, mut op: RawOp) -> Result<(), Error> { if self.sources.is_empty() { @@ -473,7 +473,7 @@ where } } - self.sources.finish(); + //self.sources.finish(); if !self.sources.sources.is_empty() { panic!("extra sources?"); diff --git a/etk-asm/tests/asm.rs b/etk-asm/tests/asm.rs index 5487409d..80346d1b 100644 --- a/etk-asm/tests/asm.rs +++ b/etk-asm/tests/asm.rs @@ -283,7 +283,7 @@ fn every_op() -> Result<(), Error> { fn every_op2() -> Result<(), Error> { let mut output = Vec::new(); let mut ingester = Ingest::new(&mut output); - ingester.ingest_file(source(&["every-op", "test.etk"]))?; + ingester.ingest_file(source(&["subdirectory", "main.etk"]))?; //assert_eq!( // output, @@ -305,18 +305,11 @@ fn every_op2() -> Result<(), Error> { //push1 label1 //jump - // [91, 88, 96, 0, 86, 96, 64, 96, 5, 96, 3|13, 96, 17|21, 97, 0, 0, 97, 0, 66, 96, 8, 97, 0, 0, 97, 0, 66, 96, 8] - println!("{:x?}", output); - println!("{:?}", output); - println!( - "Expected {:?}", - hex!("5b5860005660406005600d601561000061004260086100006100426008") - ); - - assert_eq!( - output, - hex!("5b5860005660406005600d601561000061004260086100006100426008") - ); + // [99, 192, 1, 192, 222] == [99, 192, 1, 192, 222, 96, 255] + println!("Output (hex) {:x?}", output); + println!("Output (dec) {:?}", output); + println!("Expected (dec) {:?}", hex!("63c001c0de60ff")); + assert_eq!(output, hex!("63c001c0de60ff")); Ok(()) } From ea1668b517099d03684d561c65afeaf0e6c21efe Mon Sep 17 00:00:00 2001 From: Gaston Zanitti Date: Fri, 15 Sep 2023 19:31:14 -0300 Subject: [PATCH 07/73] Fixing delayed Macros by insertion. Issue with misplaced internal labels (WIP) --- etk-asm/src/asm.rs | 90 ++++++++++++++++++++++++++++--------------- etk-asm/src/ingest.rs | 4 +- 2 files changed, 62 insertions(+), 32 deletions(-) diff --git a/etk-asm/src/asm.rs b/etk-asm/src/asm.rs index d39aae3a..ac1c5cd7 100644 --- a/etk-asm/src/asm.rs +++ b/etk-asm/src/asm.rs @@ -238,6 +238,9 @@ pub struct PendingMacro { /// The name of the macro. name: String, + /// Parameters of the macro. + parameters: Vec, + /// The offset of the macro. offset: usize, } @@ -326,10 +329,10 @@ impl Assembler { O: Into, { for op in ops { - self.push(op)?; + self.push(op, None)?; } - Ok(self.ready.len()) + Ok(self.concrete_len) } /// Insert explicilty declared macros and labels, via `AbstractOp`, and implictly declared @@ -354,12 +357,6 @@ impl Assembler { } hash_map::Entry::Vacant(v) => { v.insert(defn.to_owned()); - - for m in self.undefined_macros.iter() { - if m.name == *defn.name() { - self.ready.insert(m.offset, rop.clone()); - } - } } } } @@ -385,7 +382,7 @@ impl Assembler { /// Feed a single instruction into the `Assembler`. /// /// Returns the number of bytes that can be collected with [`Assembler::take`] - pub fn push(&mut self, rop: O) -> Result + pub fn push(&mut self, rop: O, pos: Option) -> Result where O: Into, { @@ -398,15 +395,15 @@ impl Assembler { // regardless if we `push_read` or `push_pending` -- in fact, `expand_macro` pushes each op // individually which calls the correct unchecked push. if let RawOp::Op(AbstractOp::Macro(ref m)) = rop { - self.expand_macro(&m.name, &m.parameters)?; + self.expand_macro(&m.name, &m.parameters, None)?; return Ok(self.concrete_len); } - self.push_ready(rop)?; + self.push_ready(rop, pos)?; Ok(self.concrete_len) } - fn push_ready(&mut self, rop: RawOp) -> Result<(), Error> { + fn push_ready(&mut self, rop: RawOp, pos: Option) -> Result<(), Error> { match rop { RawOp::Op(AbstractOp::Label(label)) => { let old = self @@ -416,7 +413,26 @@ impl Assembler { assert_eq!(old, None, "label should have been undefined"); Ok(()) } - RawOp::Op(AbstractOp::MacroDefinition(_)) => Ok(()), + RawOp::Op(AbstractOp::MacroDefinition(ref defn)) => { + let macros_to_expand: Vec<_> = self + .undefined_macros + .iter() + .filter(|um| um.name == *defn.name()) + .map(|um| (um.name.clone(), um.parameters.clone(), um.offset)) + .collect(); + + let expanded_names: HashSet<_> = + macros_to_expand.iter().map(|(name, _, _)| name).collect(); + + self.undefined_macros + .retain(|um| !expanded_names.contains(&um.name)); + + for (name, parameters, offset) in macros_to_expand.iter() { + self.expand_macro(name, parameters, Some(*offset))?; + } + + Ok(()) + } RawOp::Op(AbstractOp::Macro(ref m)) => Ok(()), RawOp::Op(ref op) => { match op @@ -425,7 +441,11 @@ impl Assembler { { Ok(cop) => { self.concrete_len += cop.size(); - self.ready.push(rop); + match pos { + Some(pos) => _ = self.ready.insert(pos, rop), + None => self.ready.push(rop), + } + //self.ready.push(rop); } Err(ops::Error::ExpressionTooLarge { value, spec, .. }) => { return error::ExpressionTooLarge { @@ -446,7 +466,7 @@ impl Assembler { UnknownLabel { label, .. } => { let size = match op.size() { Some(size) => size, - None => 2, // push + label + None => 2, // %push(label) }; self.concrete_len += size; self.ready.push(rop); @@ -455,14 +475,15 @@ impl Assembler { offset: self.concrete_len, }); } - UnknownMacro { name, .. } => { - self.ready.push(rop); - self.undefined_macros.push(PendingMacro { - name: name.to_owned(), - offset: self.concrete_len, - }); - } - UndefinedVariable { name, .. } => todo!(), + UnknownMacro { name, .. } => todo!("unknown macro {}", name), + //{ + // self.ready.push(rop); + // self.undefined_macros.push(PendingMacro { + // name: name.to_owned(), + // offset: self.concrete_len, + // }); + //} + UndefinedVariable { name, .. } => todo!("undefined variable {}", name), }, } @@ -470,7 +491,11 @@ impl Assembler { } RawOp::Raw(raw) => { self.concrete_len += raw.len(); - self.ready.push(RawOp::Raw(raw)); + match pos { + Some(pos) => _ = self.ready.splice(pos..pos, vec![RawOp::Raw(raw)]), + None => self.ready.push(RawOp::Raw(raw)), + } + //self.ready.push(RawOp::Raw(raw)); Ok(()) } } @@ -480,6 +505,7 @@ impl Assembler { &mut self, name: &str, parameters: &[Expression], + position: Option, ) -> Result, Error> { // Remap labels to macro scope. match self.declared_macros.get(name).cloned() { @@ -534,11 +560,18 @@ impl Assembler { } } - Ok(Some(self.push_all(m.contents)?)) + let mut pushed_size = 0; + for op in m.contents.iter().rev() { + let ps = self.push(op.clone(), position)?; + pushed_size += ps; + } + + Ok(Some(pushed_size)) } _ => { self.undefined_macros.push(PendingMacro { name: name.to_owned(), + parameters: parameters.to_owned(), offset: self.ready.len(), }); Ok(None) @@ -547,7 +580,6 @@ impl Assembler { } } -/* #[cfg(test)] mod tests { use super::*; @@ -708,9 +740,9 @@ mod tests { #[test] fn assemble_variable_push2() -> Result<(), Error> { let mut asm = Assembler::new(); - asm.push(AbstractOp::Push(Imm::with_label("auto")))?; + asm.push(AbstractOp::Push(Imm::with_label("auto")), None)?; for _ in 0..255 { - asm.push(AbstractOp::new(GetPc))?; + asm.push(AbstractOp::new(GetPc), None)?; } asm.push_all(vec![ @@ -1245,5 +1277,3 @@ mod tests { Ok(()) } } - - */ diff --git a/etk-asm/src/ingest.rs b/etk-asm/src/ingest.rs index 9bc91adc..b0be48b0 100644 --- a/etk-asm/src/ingest.rs +++ b/etk-asm/src/ingest.rs @@ -336,7 +336,7 @@ where Scope::Independent(ref mut a) => a, }; - if 0 == asm.push(op)? { + if 0 == asm.push(op, None)? { return Ok(()); } else { op = RawOp::Raw(asm.take()); @@ -348,7 +348,7 @@ where Scope::Same => panic!("sources[0] must be independent"), }; - first_asm.push(op)?; + first_asm.push(op, None)?; Ok(()) } From 53e12a34c079e5ca3810e9756f49eb3a82eb5582 Mon Sep 17 00:00:00 2001 From: Gaston Zanitti Date: Fri, 15 Sep 2023 20:46:25 -0300 Subject: [PATCH 08/73] Progress in Macro delayed, 3/34 tests failing --- etk-asm/src/asm.rs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/etk-asm/src/asm.rs b/etk-asm/src/asm.rs index ac1c5cd7..03448eb1 100644 --- a/etk-asm/src/asm.rs +++ b/etk-asm/src/asm.rs @@ -560,19 +560,24 @@ impl Assembler { } } - let mut pushed_size = 0; - for op in m.contents.iter().rev() { - let ps = self.push(op.clone(), position)?; - pushed_size += ps; - } + match position { + Some(pos) => { + let actual_size = self.ready.len(); + for op in m.contents.iter() { + let offset = self.ready.len() - actual_size + pos; + self.push(op.clone(), Some(offset))?; + } - Ok(Some(pushed_size)) + Ok(Some(self.concrete_len)) + } + None => Ok(Some(self.push_all(m.contents)?)), + } } _ => { self.undefined_macros.push(PendingMacro { name: name.to_owned(), parameters: parameters.to_owned(), - offset: self.ready.len(), + offset: self.concrete_len, }); Ok(None) } From 4df519980242bdd99376642556b8e1b74c31b312 Mon Sep 17 00:00:00 2001 From: Gaston Zanitti Date: Mon, 18 Sep 2023 11:39:46 -0300 Subject: [PATCH 09/73] Tests: 39 passed; 2 failed; Need to find a way to compute the size of "push label" ops --- etk-asm/src/asm.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/etk-asm/src/asm.rs b/etk-asm/src/asm.rs index 03448eb1..c0695e85 100644 --- a/etk-asm/src/asm.rs +++ b/etk-asm/src/asm.rs @@ -472,7 +472,7 @@ impl Assembler { self.ready.push(rop); self.undefined_labels.push(PendingLabel { label: label.to_owned(), - offset: self.concrete_len, + offset: self.ready.len(), }); } UnknownMacro { name, .. } => todo!("unknown macro {}", name), @@ -577,7 +577,7 @@ impl Assembler { self.undefined_macros.push(PendingMacro { name: name.to_owned(), parameters: parameters.to_owned(), - offset: self.concrete_len, + offset: self.ready.len(), }); Ok(None) } From 2be1ee3073daba2d0e51014c0140ef73e3019778 Mon Sep 17 00:00:00 2001 From: Gaston Zanitti Date: Mon, 18 Sep 2023 18:21:06 -0300 Subject: [PATCH 10/73] ingest_import_in_include failing. Need to check concrete_len during include/import --- etk-asm/src/asm.rs | 32 ++++++++++++++++++++------------ etk-asm/tests/asm.rs | 35 ----------------------------------- 2 files changed, 20 insertions(+), 47 deletions(-) diff --git a/etk-asm/src/asm.rs b/etk-asm/src/asm.rs index c0695e85..14c1de19 100644 --- a/etk-asm/src/asm.rs +++ b/etk-asm/src/asm.rs @@ -136,6 +136,7 @@ use crate::ops::{self, AbstractOp, Assemble, Expression, Imm, MacroDefinition}; use etk_ops::cancun::{Op, Push1}; use rand::Rng; use snafu::OptionExt; +use std::cmp; use std::collections::{hash_map, HashMap, HashSet, VecDeque}; /// An item to be assembled, which can be either an [`AbstractOp`] or a raw byte @@ -346,7 +347,6 @@ impl Assembler { } hash_map::Entry::Vacant(v) => { v.insert(None); - self.undefined_labels.retain(|l| l.label != *label); } } } @@ -365,7 +365,7 @@ impl Assembler { // Get all labels used by `rop`, check if they've been defined, and if not, note them as // "undeclared". - /*if let Some(Ok(labels)) = rop.expr().map(|e| e.labels(&self.declared_macros)) { + if let Some(Ok(labels)) = rop.expr().map(|e| e.labels(&self.declared_macros)) { for label in labels { if !self.declared_labels.contains_key(&label) { self.undefined_labels.push(PendingLabel { @@ -374,7 +374,7 @@ impl Assembler { }); } } - }*/ + } Ok(()) } @@ -406,9 +406,19 @@ impl Assembler { fn push_ready(&mut self, rop: RawOp, pos: Option) -> Result<(), Error> { match rop { RawOp::Op(AbstractOp::Label(label)) => { + let mut dst = 0; + for ul in self.undefined_labels.iter() { + if ul.label == label { + let tmp = ((self.concrete_len - ul.offset) / 256) as f32; + dst = cmp::max(tmp.floor() as usize, dst) + } + } + + self.undefined_labels.retain(|l| l.label != *label); + let old = self .declared_labels - .insert(label, Some(self.concrete_len)) + .insert(label, Some(self.concrete_len + dst)) .expect("label should exist"); assert_eq!(old, None, "label should have been undefined"); Ok(()) @@ -433,7 +443,7 @@ impl Assembler { Ok(()) } - RawOp::Op(AbstractOp::Macro(ref m)) => Ok(()), + RawOp::Op(AbstractOp::Macro(_)) => Ok(()), RawOp::Op(ref op) => { match op .clone() @@ -445,7 +455,6 @@ impl Assembler { Some(pos) => _ = self.ready.insert(pos, rop), None => self.ready.push(rop), } - //self.ready.push(rop); } Err(ops::Error::ExpressionTooLarge { value, spec, .. }) => { return error::ExpressionTooLarge { @@ -466,14 +475,14 @@ impl Assembler { UnknownLabel { label, .. } => { let size = match op.size() { Some(size) => size, - None => 2, // %push(label) + None => 2, // %push(label) with min size }; self.concrete_len += size; self.ready.push(rop); - self.undefined_labels.push(PendingLabel { - label: label.to_owned(), - offset: self.ready.len(), - }); + //self.undefined_labels.push(PendingLabel { + // label: label.to_owned(), + // offset: self.ready.len(), + //}); } UnknownMacro { name, .. } => todo!("unknown macro {}", name), //{ @@ -495,7 +504,6 @@ impl Assembler { Some(pos) => _ = self.ready.splice(pos..pos, vec![RawOp::Raw(raw)]), None => self.ready.push(RawOp::Raw(raw)), } - //self.ready.push(RawOp::Raw(raw)); Ok(()) } } diff --git a/etk-asm/tests/asm.rs b/etk-asm/tests/asm.rs index 80346d1b..622519f9 100644 --- a/etk-asm/tests/asm.rs +++ b/etk-asm/tests/asm.rs @@ -278,38 +278,3 @@ fn every_op() -> Result<(), Error> { Ok(()) } - -#[test] -fn every_op2() -> Result<(), Error> { - let mut output = Vec::new(); - let mut ingester = Ingest::new(&mut output); - ingester.ingest_file(source(&["subdirectory", "main.etk"]))?; - - //assert_eq!( - // output, - // hex!( - // " - // 00 - //" - // ) - //); - - // 5b: jumpdest - // 60: push1 - // 56: jump - - //label1: - // djumpdest - // push1 0x01 - - //push1 label1 - //jump - - // [99, 192, 1, 192, 222] == [99, 192, 1, 192, 222, 96, 255] - println!("Output (hex) {:x?}", output); - println!("Output (dec) {:?}", output); - println!("Expected (dec) {:?}", hex!("63c001c0de60ff")); - assert_eq!(output, hex!("63c001c0de60ff")); - - Ok(()) -} From b6b23ba65b351d3a077332958f9949b83b1be181 Mon Sep 17 00:00:00 2001 From: Gaston Zanitti Date: Tue, 19 Sep 2023 11:24:58 -0300 Subject: [PATCH 11/73] cargo test: all tests passing :tada:. Code needs to be cleaned before merge --- etk-asm/src/asm.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/etk-asm/src/asm.rs b/etk-asm/src/asm.rs index 14c1de19..8e41b06d 100644 --- a/etk-asm/src/asm.rs +++ b/etk-asm/src/asm.rs @@ -271,7 +271,7 @@ impl Assembler { match output { Ok(v) => { self.ready.clear(); - self.concrete_len = 0; + //self.concrete_len = 0; v } Err(e) => { @@ -410,7 +410,7 @@ impl Assembler { for ul in self.undefined_labels.iter() { if ul.label == label { let tmp = ((self.concrete_len - ul.offset) / 256) as f32; - dst = cmp::max(tmp.floor() as usize, dst) + dst = cmp::max(tmp.floor() as usize, dst); } } From 03a49909348f9c2c22d85e6780ddde5a24ca1e59 Mon Sep 17 00:00:00 2001 From: Gaston Zanitti Date: Tue, 19 Sep 2023 13:51:02 -0300 Subject: [PATCH 12/73] Issue 124 has been fixed. A new test has been added to ensure that it remains correct over time. --- etk-asm/src/asm.rs | 64 +++++++++++++++---- etk-asm/src/ingest.rs | 2 +- etk-asm/tests/asm.rs | 23 ++++++- .../undefined-label-undefined-macro.etk | 6 ++ 4 files changed, 80 insertions(+), 15 deletions(-) create mode 100644 etk-asm/tests/asm/instruction-macro/undefined-label-undefined-macro.etk diff --git a/etk-asm/src/asm.rs b/etk-asm/src/asm.rs index 8e41b06d..836c65da 100644 --- a/etk-asm/src/asm.rs +++ b/etk-asm/src/asm.rs @@ -202,9 +202,12 @@ impl From> for RawOp { /// ``` #[derive(Debug)] pub struct Assembler { - /// Assembled ops, ready to be taken. + /// Assembled ops, not yet ready to be taken. ready: Vec, + /// Assembled ops, ready to be taken. + output: Vec, + /// Number ob bytes in 'ready' that are ready to be taken. concrete_len: usize, @@ -224,7 +227,7 @@ pub struct Assembler { } /// TODO Temporal DOC -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct PendingLabel { /// The name of the label. label: String, @@ -234,7 +237,7 @@ pub struct PendingLabel { } /// TODO Temporal DOC -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct PendingMacro { /// The name of the macro. name: String, @@ -250,6 +253,7 @@ impl Default for Assembler { fn default() -> Self { Self { ready: Default::default(), + output: Default::default(), concrete_len: 0, declared_labels: Default::default(), declared_macros: Default::default(), @@ -267,11 +271,12 @@ impl Assembler { /// Collect any assembled instructions that are ready to be output. pub fn take(&mut self) -> Vec { - let output = self.finish(); + let output = self.concretize_ops(); match output { Ok(v) => { self.ready.clear(); //self.concrete_len = 0; + //self.output.clear(); v } Err(e) => { @@ -282,7 +287,45 @@ impl Assembler { } /// Temporal documentation. TODO - pub fn finish(&mut self) -> Result, Error> { + fn concretize_ops(&mut self) -> Result, Error> { + let mut output = Vec::new(); + for op in self.ready.iter() { + if let RawOp::Op(ref op) = op { + match op + .clone() + .concretize((&self.declared_labels, &self.declared_macros).into()) + { + Ok(cop) => cop.assemble(&mut output), + Err(ops::Error::ContextIncomplete { source }) => match source { + UnknownLabel { label: _label, .. } => { + let undefined_names: Vec<_> = self + .undefined_labels + .iter() + .map(|PendingLabel { label, .. }| label.clone()) + .collect(); + return error::UndeclaredLabels { + labels: undefined_names, + } + .fail(); + } + UnknownMacro { name, .. } => { + return error::UndeclaredInstructionMacro { name }.fail(); + } + + UndefinedVariable { name, .. } => todo!("undefined variable {}", name), + }, + Err(_) => unreachable!("all ops should be concretizable"), + } + } else if let RawOp::Raw(raw) = op { + output.extend(raw); + } + } + + Ok(output) + } + + /// Temporal documentation. TODO + pub fn finish(&mut self) -> Result<(), Error> { if !self.undefined_labels.is_empty() { return error::UndeclaredLabels { labels: self @@ -302,23 +345,22 @@ impl Assembler { } // Concretize every RawOp ready, and collect the results. - let mut output = Vec::new(); - for ready_op in self.ready.iter() { + /*for ready_op in self.ready.iter() { if let RawOp::Op(ref op) = ready_op { match op .clone() .concretize((&self.declared_labels, &self.declared_macros).into()) { - Ok(cop) => cop.assemble(&mut output), + Ok(cop) => cop.assemble(&mut self.output), Err(e) => unreachable!("all ops should be concretizable: {}", e), } } else if let RawOp::Raw(raw) = ready_op { self.concrete_len += raw.len(); - output.extend(raw); + self.output.extend(raw); } - } + }*/ - Ok(output) + Ok(()) } /// Feed instructions into the `Assembler`. diff --git a/etk-asm/src/ingest.rs b/etk-asm/src/ingest.rs index b0be48b0..dcf5922b 100644 --- a/etk-asm/src/ingest.rs +++ b/etk-asm/src/ingest.rs @@ -297,7 +297,7 @@ where }; let raw = asm.take(); - //asm.finish()?; + asm.finish()?; if raw.is_empty() { return Ok(()); diff --git a/etk-asm/tests/asm.rs b/etk-asm/tests/asm.rs index 622519f9..c7ccc7f8 100644 --- a/etk-asm/tests/asm.rs +++ b/etk-asm/tests/asm.rs @@ -1,6 +1,9 @@ use assert_matches::assert_matches; -use etk_asm::ingest::{Error, Ingest}; +use etk_asm::{ + asm::Assembler, + ingest::{Error, Ingest}, +}; use hex_literal::hex; @@ -75,8 +78,6 @@ fn instruction_macro() -> Result<(), Error> { let mut ingester = Ingest::new(&mut output); ingester.ingest_file(source(&["instruction-macro", "main.etk"]))?; - println!("{:x?}", output); - assert_eq!( output, hex!("5b5860005660406005600d601561000061004260086100006100426008") @@ -110,6 +111,22 @@ fn instruction_macro_with_two_instructions_per_line() { assert_matches!(err, Error::Parse { .. }); } +#[test] +fn undefined_label_undefined_macro() { + let mut output = Vec::new(); + let mut ingester = Ingest::new(&mut output); + let err = ingester + .ingest_file(source(&[ + "instruction-macro", + "undefined-label-undefined-macro.etk", + ])) + .unwrap_err(); + + assert_matches!(err, etk_asm::ingest::Error::Assemble { source: + etk_asm::asm::Error::UndeclaredLabels { labels, .. }, .. + } if labels == vec!["revert".to_string()]); +} + #[test] fn every_op() -> Result<(), Error> { let mut output = Vec::new(); diff --git a/etk-asm/tests/asm/instruction-macro/undefined-label-undefined-macro.etk b/etk-asm/tests/asm/instruction-macro/undefined-label-undefined-macro.etk new file mode 100644 index 00000000..4103fb6b --- /dev/null +++ b/etk-asm/tests/asm/instruction-macro/undefined-label-undefined-macro.etk @@ -0,0 +1,6 @@ +%macro revert_if_neq() + push1 revert +%end + +%revert_if_neq() +%revert() \ No newline at end of file From 93c66c94fdd37b0451ad5995ce2c8251171fe7d7 Mon Sep 17 00:00:00 2001 From: Gaston Zanitti Date: Tue, 19 Sep 2023 14:12:46 -0300 Subject: [PATCH 13/73] Code cleaning --- etk-asm/src/asm.rs | 79 ++++++++++++------------------------------- etk-asm/src/ingest.rs | 13 ------- etk-asm/tests/asm.rs | 5 +-- 3 files changed, 22 insertions(+), 75 deletions(-) diff --git a/etk-asm/src/asm.rs b/etk-asm/src/asm.rs index 836c65da..f0883ed8 100644 --- a/etk-asm/src/asm.rs +++ b/etk-asm/src/asm.rs @@ -131,13 +131,10 @@ mod error { pub use self::error::Error; use crate::ops::expression::Error::{UndefinedVariable, UnknownLabel, UnknownMacro}; -use crate::ops::expression::{self, Terminal}; -use crate::ops::{self, AbstractOp, Assemble, Expression, Imm, MacroDefinition}; -use etk_ops::cancun::{Op, Push1}; +use crate::ops::{self, AbstractOp, Assemble, Expression, MacroDefinition}; use rand::Rng; -use snafu::OptionExt; use std::cmp; -use std::collections::{hash_map, HashMap, HashSet, VecDeque}; +use std::collections::{hash_map, HashMap, HashSet}; /// An item to be assembled, which can be either an [`AbstractOp`] or a raw byte /// sequence. @@ -205,9 +202,6 @@ pub struct Assembler { /// Assembled ops, not yet ready to be taken. ready: Vec, - /// Assembled ops, ready to be taken. - output: Vec, - /// Number ob bytes in 'ready' that are ready to be taken. concrete_len: usize, @@ -226,17 +220,17 @@ pub struct Assembler { undefined_macros: Vec, } -/// TODO Temporal DOC +/// Struct used to keep track of pending label invocations and their positions in code. #[derive(Debug, Clone)] pub struct PendingLabel { /// The name of the label. label: String, - /// The offset of the label. - offset: usize, + /// Concrete position where the label was invoked. + position: usize, } -/// TODO Temporal DOC +/// Struct used to keep track of pending macro invocations and thwey positions in code. #[derive(Debug, Clone)] pub struct PendingMacro { /// The name of the macro. @@ -245,15 +239,14 @@ pub struct PendingMacro { /// Parameters of the macro. parameters: Vec, - /// The offset of the macro. - offset: usize, + /// Concrete position where the macro was invoked. + position: usize, } impl Default for Assembler { fn default() -> Self { Self { ready: Default::default(), - output: Default::default(), concrete_len: 0, declared_labels: Default::default(), declared_macros: Default::default(), @@ -275,8 +268,6 @@ impl Assembler { match output { Ok(v) => { self.ready.clear(); - //self.concrete_len = 0; - //self.output.clear(); v } Err(e) => { @@ -286,7 +277,7 @@ impl Assembler { } } - /// Temporal documentation. TODO + /// Concretize all assembled instructions. fn concretize_ops(&mut self) -> Result, Error> { let mut output = Vec::new(); for op in self.ready.iter() { @@ -324,7 +315,8 @@ impl Assembler { Ok(output) } - /// Temporal documentation. TODO + /// Indicate that the input sequence is complete. Returns any errors that + /// may remain. pub fn finish(&mut self) -> Result<(), Error> { if !self.undefined_labels.is_empty() { return error::UndeclaredLabels { @@ -344,22 +336,6 @@ impl Assembler { .fail(); } - // Concretize every RawOp ready, and collect the results. - /*for ready_op in self.ready.iter() { - if let RawOp::Op(ref op) = ready_op { - match op - .clone() - .concretize((&self.declared_labels, &self.declared_macros).into()) - { - Ok(cop) => cop.assemble(&mut self.output), - Err(e) => unreachable!("all ops should be concretizable: {}", e), - } - } else if let RawOp::Raw(raw) = ready_op { - self.concrete_len += raw.len(); - self.output.extend(raw); - } - }*/ - Ok(()) } @@ -412,7 +388,7 @@ impl Assembler { if !self.declared_labels.contains_key(&label) { self.undefined_labels.push(PendingLabel { label: label.to_owned(), - offset: self.ready.len(), + position: self.ready.len(), }); } } @@ -429,13 +405,10 @@ impl Assembler { O: Into, { let rop = rop.into(); - println!("pushing {:?}", rop); self.declare_content(&rop)?; - // Expand instruction macros immediately. We do this here because it's the same process - // regardless if we `push_read` or `push_pending` -- in fact, `expand_macro` pushes each op - // individually which calls the correct unchecked push. + // Expand instruction macros immediately. if let RawOp::Op(AbstractOp::Macro(ref m)) = rop { self.expand_macro(&m.name, &m.parameters, None)?; return Ok(self.concrete_len); @@ -451,7 +424,7 @@ impl Assembler { let mut dst = 0; for ul in self.undefined_labels.iter() { if ul.label == label { - let tmp = ((self.concrete_len - ul.offset) / 256) as f32; + let tmp = ((self.concrete_len - ul.position) / 256) as f32; dst = cmp::max(tmp.floor() as usize, dst); } } @@ -470,7 +443,7 @@ impl Assembler { .undefined_macros .iter() .filter(|um| um.name == *defn.name()) - .map(|um| (um.name.clone(), um.parameters.clone(), um.offset)) + .map(|um| (um.name.clone(), um.parameters.clone(), um.position)) .collect(); let expanded_names: HashSet<_> = @@ -514,26 +487,17 @@ impl Assembler { .fail() } Err(ops::Error::ContextIncomplete { source }) => match source { - UnknownLabel { label, .. } => { + UnknownLabel { label: _label, .. } => { let size = match op.size() { Some(size) => size, - None => 2, // %push(label) with min size + None => 2, // %push(label) with min size (to be updated) }; self.concrete_len += size; self.ready.push(rop); - //self.undefined_labels.push(PendingLabel { - // label: label.to_owned(), - // offset: self.ready.len(), - //}); } - UnknownMacro { name, .. } => todo!("unknown macro {}", name), - //{ - // self.ready.push(rop); - // self.undefined_macros.push(PendingMacro { - // name: name.to_owned(), - // offset: self.concrete_len, - // }); - //} + UnknownMacro { name, .. } => { + return error::UndeclaredInstructionMacro { name }.fail() + } UndefinedVariable { name, .. } => todo!("undefined variable {}", name), }, } @@ -575,7 +539,6 @@ impl Assembler { // First pass, find locally defined labels and rename them. for op in m.contents.iter_mut() { - println!("macro {:?} op: {:?}", name, op); match op { AbstractOp::Label(ref mut label) => { let mangled = format!("{}_{}_{}", m.name, label, rng.gen::()); @@ -627,7 +590,7 @@ impl Assembler { self.undefined_macros.push(PendingMacro { name: name.to_owned(), parameters: parameters.to_owned(), - offset: self.ready.len(), + position: self.ready.len(), }); Ok(None) } diff --git a/etk-asm/src/ingest.rs b/etk-asm/src/ingest.rs index dcf5922b..ac5d9cfe 100644 --- a/etk-asm/src/ingest.rs +++ b/etk-asm/src/ingest.rs @@ -314,17 +314,6 @@ where } } - /*fn finish(&mut self) { - for frame in self.sources.iter_mut().rev() { - let asm = match frame.scope { - Scope::Same => continue, - Scope::Independent(ref mut a) => a, - }; - - asm.finish().unwrap(); - } - }*/ - fn write(&mut self, mut op: RawOp) -> Result<(), Error> { if self.sources.is_empty() { panic!("no sources!"); @@ -473,8 +462,6 @@ where } } - //self.sources.finish(); - if !self.sources.sources.is_empty() { panic!("extra sources?"); } diff --git a/etk-asm/tests/asm.rs b/etk-asm/tests/asm.rs index c7ccc7f8..ed2884af 100644 --- a/etk-asm/tests/asm.rs +++ b/etk-asm/tests/asm.rs @@ -1,9 +1,6 @@ use assert_matches::assert_matches; -use etk_asm::{ - asm::Assembler, - ingest::{Error, Ingest}, -}; +use etk_asm::ingest::{Error, Ingest}; use hex_literal::hex; From a7c1d962c3b37359837325d30f90895f7be5d693 Mon Sep 17 00:00:00 2001 From: Gaston Zanitti Date: Tue, 19 Sep 2023 14:45:41 -0300 Subject: [PATCH 14/73] push_ready renamed to push_rawop --- etk-asm/src/asm.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/etk-asm/src/asm.rs b/etk-asm/src/asm.rs index f0883ed8..2a9bdc18 100644 --- a/etk-asm/src/asm.rs +++ b/etk-asm/src/asm.rs @@ -414,11 +414,11 @@ impl Assembler { return Ok(self.concrete_len); } - self.push_ready(rop, pos)?; + self.push_rawop(rop, pos)?; Ok(self.concrete_len) } - fn push_ready(&mut self, rop: RawOp, pos: Option) -> Result<(), Error> { + fn push_rawop(&mut self, rop: RawOp, pos: Option) -> Result<(), Error> { match rop { RawOp::Op(AbstractOp::Label(label)) => { let mut dst = 0; From 6f0634a8398070898ed72b5c829be70afe7b89c7 Mon Sep 17 00:00:00 2001 From: Gaston Zanitti Date: Tue, 19 Sep 2023 20:02:10 -0300 Subject: [PATCH 15/73] Rustfmt --- etk-asm/src/asm.rs | 29 +++-------------------------- 1 file changed, 3 insertions(+), 26 deletions(-) diff --git a/etk-asm/src/asm.rs b/etk-asm/src/asm.rs index 2a9bdc18..18b2f216 100644 --- a/etk-asm/src/asm.rs +++ b/etk-asm/src/asm.rs @@ -149,13 +149,6 @@ pub enum RawOp { } impl RawOp { - fn size(&self) -> Option { - match self { - Self::Op(op) => op.size(), - Self::Raw(raw) => Some(raw.len()), - } - } - fn expr(&self) -> Option<&Expression> { match self { Self::Op(op) => op.expr(), @@ -197,7 +190,7 @@ impl From> for RawOp { /// # assert_eq!(output, hex!("58")); /// # Result::<(), Error>::Ok(()) /// ``` -#[derive(Debug)] +#[derive(Debug, Default)] pub struct Assembler { /// Assembled ops, not yet ready to be taken. ready: Vec, @@ -243,19 +236,6 @@ pub struct PendingMacro { position: usize, } -impl Default for Assembler { - fn default() -> Self { - Self { - ready: Default::default(), - concrete_len: 0, - declared_labels: Default::default(), - declared_macros: Default::default(), - undefined_labels: Default::default(), - undefined_macros: Default::default(), - } - } -} - impl Assembler { /// Create a new `Assembler`. pub fn new() -> Self { @@ -467,7 +447,7 @@ impl Assembler { Ok(cop) => { self.concrete_len += cop.size(); match pos { - Some(pos) => _ = self.ready.insert(pos, rop), + Some(pos) => self.ready.insert(pos, rop), None => self.ready.push(rop), } } @@ -488,10 +468,7 @@ impl Assembler { } Err(ops::Error::ContextIncomplete { source }) => match source { UnknownLabel { label: _label, .. } => { - let size = match op.size() { - Some(size) => size, - None => 2, // %push(label) with min size (to be updated) - }; + let size = op.size().unwrap_or(2); // %push(label) with min size (to be updated) self.concrete_len += size; self.ready.push(rop); } From da7afe05e2ef7728a60ea49b512872955b4bb679 Mon Sep 17 00:00:00 2001 From: Gaston Zanitti Date: Tue, 19 Sep 2023 20:42:12 -0300 Subject: [PATCH 16/73] Rustfmt in dasm (not related but fixed) --- etk-dasm/src/bin/disease.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/etk-dasm/src/bin/disease.rs b/etk-dasm/src/bin/disease.rs index 7729d9e0..08afb442 100644 --- a/etk-dasm/src/bin/disease.rs +++ b/etk-dasm/src/bin/disease.rs @@ -55,10 +55,7 @@ fn run() -> Result<(), Error> { separator.push_all(disasm.ops()); - let basic_blocks = separator - .take() - .into_iter() - .chain(separator.finish().into_iter()); + let basic_blocks = separator.take().into_iter().chain(separator.finish()); for block in basic_blocks { let mut offset = block.offset; From 5530125fa711709132a5dc49575eeb825e6a05b4 Mon Sep 17 00:00:00 2001 From: Gaston Zanitti Date: Tue, 19 Sep 2023 22:23:53 -0300 Subject: [PATCH 17/73] Rustfmt in ecfg (not related but fixed) --- etk-analyze/src/bin/ecfg.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etk-analyze/src/bin/ecfg.rs b/etk-analyze/src/bin/ecfg.rs index a8fd7a37..7f963c3b 100644 --- a/etk-analyze/src/bin/ecfg.rs +++ b/etk-analyze/src/bin/ecfg.rs @@ -58,7 +58,7 @@ fn run() -> Result<(), Error> { let blocks = separator .take() .into_iter() - .chain(separator.finish().into_iter()) + .chain(separator.finish()) .map(|x| AnnotatedBlock::annotate(&x)); let mut cfg = ControlFlowGraph::new(blocks); From 2114ffe28ebb973c6aea7301402715f95c9a1aed Mon Sep 17 00:00:00 2001 From: Gaston Zanitti Date: Wed, 27 Sep 2023 10:50:06 -0300 Subject: [PATCH 18/73] Output cleaning --- etk-asm/src/asm.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/etk-asm/src/asm.rs b/etk-asm/src/asm.rs index 18b2f216..f4c45287 100644 --- a/etk-asm/src/asm.rs +++ b/etk-asm/src/asm.rs @@ -250,10 +250,7 @@ impl Assembler { self.ready.clear(); v } - Err(e) => { - eprintln!("error: {}", e); - Vec::new() - } + Err(_) => Vec::new(), } } From f1bb8841b85eb3af4907d1551ece6172e132632a Mon Sep 17 00:00:00 2001 From: Gaston Zanitti Date: Mon, 2 Oct 2023 10:59:46 -0300 Subject: [PATCH 19/73] Several changes to simplify the compiler. It's still a PoC: the code is messy and there are several hacks I need to remove. Also I should write a more rusty code. --- etk-asm/src/asm.rs | 112 ++++++++++++++++++++++++------------ etk-asm/src/ast.rs | 9 ++- etk-asm/src/parse/error.rs | 35 +++++++++++ etk-asm/src/parse/macros.rs | 47 +++++++++++++-- etk-asm/src/parse/mod.rs | 14 +++-- etk-asm/tests/asm.rs | 16 +++++- 6 files changed, 177 insertions(+), 56 deletions(-) diff --git a/etk-asm/src/asm.rs b/etk-asm/src/asm.rs index f4c45287..c2d467c4 100644 --- a/etk-asm/src/asm.rs +++ b/etk-asm/src/asm.rs @@ -130,6 +130,7 @@ mod error { } pub use self::error::Error; +use crate::ast::Node; use crate::ops::expression::Error::{UndefinedVariable, UnknownLabel, UnknownMacro}; use crate::ops::{self, AbstractOp, Assemble, Expression, MacroDefinition}; use rand::Rng; @@ -195,7 +196,7 @@ pub struct Assembler { /// Assembled ops, not yet ready to be taken. ready: Vec, - /// Number ob bytes in 'ready' that are ready to be taken. + /// Number of bytes in 'ready' that are ready to be taken. concrete_len: usize, /// Labels, in `pending`, associated with an `AbstractOp::Label`. @@ -219,11 +220,11 @@ pub struct PendingLabel { /// The name of the label. label: String, - /// Concrete position where the label was invoked. + /// Position where the label was invoked. position: usize, } -/// Struct used to keep track of pending macro invocations and thwey positions in code. +/// Struct used to keep track of pending macro invocations and their positions in code. #[derive(Debug, Clone)] pub struct PendingMacro { /// The name of the macro. @@ -232,7 +233,7 @@ pub struct PendingMacro { /// Parameters of the macro. parameters: Vec, - /// Concrete position where the macro was invoked. + /// Position where the macro was invoked. position: usize, } @@ -242,6 +243,35 @@ impl Assembler { Self::default() } + /// TODO: Add DOC + pub fn new_internal(concrete_len: usize) -> Self { + Self { + concrete_len, + ..Self::default() + } + } + + /// TODO: Add DOC + pub fn get_concrete_len(&self) -> usize { + self.concrete_len + } + + /// TODO: Add DOC + pub fn inspect_macros(&mut self, nodes: I) -> Result<(), Error> + where + I: IntoIterator, + O: Into, + { + for op in nodes { + let op = op.into(); + if let RawOp::Op(AbstractOp::MacroDefinition(_)) = op { + self.declare_content(op)? + } + } + + Ok(()) + } + /// Collect any assembled instructions that are ready to be output. pub fn take(&mut self) -> Vec { let output = self.concretize_ops(); @@ -333,7 +363,11 @@ impl Assembler { /// Insert explicilty declared macros and labels, via `AbstractOp`, and implictly declared /// macros and labels via usage in `Op`. - fn declare_content(&mut self, rop: &RawOp) -> Result<(), Error> { + pub fn declare_content(&mut self, rop: O) -> Result<(), Error> + where + O: Into, + { + let rop = rop.into(); match rop { RawOp::Op(AbstractOp::Label(ref label)) => { match self.declared_labels.entry(label.to_owned()) { @@ -382,8 +416,19 @@ impl Assembler { O: Into, { let rop = rop.into(); + println!("pushing {:?}", rop); - self.declare_content(&rop)?; + //self.declare_content(&rop)?; + if let RawOp::Op(AbstractOp::Label(ref label)) = rop { + match self.declared_labels.entry(label.to_owned()) { + hash_map::Entry::Occupied(_) => { + return error::DuplicateLabel { label }.fail(); + } + hash_map::Entry::Vacant(v) => { + v.insert(None); + } + } + } // Expand instruction macros immediately. if let RawOp::Op(AbstractOp::Macro(ref m)) = rop { @@ -415,26 +460,7 @@ impl Assembler { assert_eq!(old, None, "label should have been undefined"); Ok(()) } - RawOp::Op(AbstractOp::MacroDefinition(ref defn)) => { - let macros_to_expand: Vec<_> = self - .undefined_macros - .iter() - .filter(|um| um.name == *defn.name()) - .map(|um| (um.name.clone(), um.parameters.clone(), um.position)) - .collect(); - - let expanded_names: HashSet<_> = - macros_to_expand.iter().map(|(name, _, _)| name).collect(); - - self.undefined_macros - .retain(|um| !expanded_names.contains(&um.name)); - - for (name, parameters, offset) in macros_to_expand.iter() { - self.expand_macro(name, parameters, Some(*offset))?; - } - - Ok(()) - } + RawOp::Op(AbstractOp::MacroDefinition(_)) => Ok(()), RawOp::Op(AbstractOp::Macro(_)) => Ok(()), RawOp::Op(ref op) => { match op @@ -467,6 +493,11 @@ impl Assembler { UnknownLabel { label: _label, .. } => { let size = op.size().unwrap_or(2); // %push(label) with min size (to be updated) self.concrete_len += size; + + self.undefined_labels.push(PendingLabel { + label: _label.to_owned(), + position: self.ready.len(), + }); self.ready.push(rop); } UnknownMacro { name, .. } => { @@ -555,19 +586,12 @@ impl Assembler { self.push(op.clone(), Some(offset))?; } - Ok(Some(self.concrete_len)) + Ok(Some(self.ready.len())) } None => Ok(Some(self.push_all(m.contents)?)), } } - _ => { - self.undefined_macros.push(PendingMacro { - name: name.to_owned(), - parameters: parameters.to_owned(), - position: self.ready.len(), - }); - Ok(None) - } + _ => return error::UndeclaredInstructionMacro { name }.fail(), } } } @@ -897,6 +921,7 @@ mod tests { ]; let mut asm = Assembler::new(); + asm.inspect_macros(ops.clone())?; let sz = asm.push_all(ops)?; assert_eq!(sz, 0); let out = asm.take(); @@ -933,6 +958,7 @@ mod tests { ]; let mut asm = Assembler::new(); + asm.inspect_macros(ops.clone())?; let sz = asm.push_all(ops)?; assert_eq!(sz, 13); let out = asm.take(); @@ -965,6 +991,7 @@ mod tests { ]; let mut asm = Assembler::new(); + asm.inspect_macros(ops.clone())?; let sz = asm.push_all(ops)?; assert_eq!(sz, 8); let out = asm.take(); @@ -997,6 +1024,7 @@ mod tests { ]; let mut asm = Assembler::new(); + asm.inspect_macros(ops.clone())?; let sz = asm.push_all(ops)?; assert_eq!(sz, 8); let out = asm.take(); @@ -1029,6 +1057,7 @@ mod tests { ]; let mut asm = Assembler::new(); + asm.inspect_macros(ops.clone())?; let sz = asm.push_all(ops)?; assert_eq!(7, sz); assert_eq!(asm.take(), hex!("5b600560065858")); @@ -1042,8 +1071,10 @@ mod tests { InstructionMacroInvocation::with_zero_parameters("my_macro".into()), )]; let mut asm = Assembler::new(); - asm.push_all(ops)?; - let err = asm.finish().unwrap_err(); + asm.inspect_macros(ops.clone())?; + let err = asm.push_all(ops).unwrap_err(); + //let err = asm.finish().unwrap_err(); + println!("{:?}", err); assert_matches!(err, Error::UndeclaredInstructionMacro { name, .. } if name == "my_macro"); Ok(()) @@ -1066,7 +1097,8 @@ mod tests { .into(), ]; let mut asm = Assembler::new(); - let err = asm.push_all(ops).unwrap_err(); + let err = asm.inspect_macros(ops.clone()).unwrap_err(); + //let err = asm.push_all(ops).unwrap_err(); assert_matches!(err, Error::DuplicateMacro { name, .. } if name == "my_macro"); Ok(()) @@ -1086,6 +1118,7 @@ mod tests { )), ]; let mut asm = Assembler::new(); + asm.inspect_macros(ops.clone())?; let err = asm.push_all(ops).unwrap_err(); assert_matches!(err, Error::DuplicateLabel { label, .. } if label == "a"); @@ -1113,6 +1146,7 @@ mod tests { AbstractOp::new(Push1(Imm::with_label("a"))), ]; let mut asm = Assembler::new(); + asm.inspect_macros(ops.clone())?; let sz = asm.push_all(ops)?; assert_eq!(sz, 5); @@ -1149,6 +1183,7 @@ mod tests { ]; let mut asm = Assembler::new(); + asm.inspect_macros(ops.clone())?; let sz = asm.push_all(ops)?; assert_eq!(sz, 7); let out = asm.take(); @@ -1261,6 +1296,7 @@ mod tests { ]; let mut asm = Assembler::new(); + asm.inspect_macros(ops.clone())?; let sz = asm.push_all(ops)?; assert_eq!(sz, 2); let out = asm.take(); diff --git a/etk-asm/src/ast.rs b/etk-asm/src/ast.rs index a4d2e15d..8c93104f 100644 --- a/etk-asm/src/ast.rs +++ b/etk-asm/src/ast.rs @@ -1,14 +1,13 @@ use crate::ops::{Abstract, AbstractOp, ExpressionMacroDefinition, InstructionMacroDefinition}; use etk_ops::cancun::Op; -use std::path::PathBuf; #[derive(Debug, Clone, PartialEq)] pub(crate) enum Node { Op(AbstractOp), - Raw(Vec), - Import(PathBuf), - Include(PathBuf), - IncludeHex(PathBuf), + //Raw(Vec), + //Import(Vec), + Include(Vec), + IncludeHex(Vec), } impl From> for Node { diff --git a/etk-asm/src/parse/error.rs b/etk-asm/src/parse/error.rs index ccc89e9e..b4e5bebb 100644 --- a/etk-asm/src/parse/error.rs +++ b/etk-asm/src/parse/error.rs @@ -1,3 +1,5 @@ +use std::path::PathBuf; + use pest::error::Error; use snafu::{Backtrace, IntoError, Snafu}; @@ -60,6 +62,39 @@ pub enum ParseError { /// The location of the error. backtrace: Backtrace, }, + + /// An argument provided to a macro was of the wrong type. + #[snafu(display("File {} does not exist", path.to_string_lossy()))] + #[non_exhaustive] + FileNotFound { + /// Path to the offending file. + path: PathBuf, + + /// The location of the error. + backtrace: Backtrace, + }, + + /// An included fail failed to parse as hexadecimal. + #[snafu(display("included file `{}` is invalid hex: {}", path.to_string_lossy(), source))] + #[non_exhaustive] + InvalidHex { + /// Path to the offending file. + path: PathBuf, + + /// The underlying source of this error. + source: Box, + + /// The location of the error. + backtrace: Backtrace, + }, + + /// A recursion limit was reached while including or importing a file. + #[snafu(display("too many levels of recursion/includes"))] + #[non_exhaustive] + RecursionLimit { + /// The location of the error. + backtrace: Backtrace, + }, } impl From> for ParseError { diff --git a/etk-asm/src/parse/macros.rs b/etk-asm/src/parse/macros.rs index c262917f..63633834 100644 --- a/etk-asm/src/parse/macros.rs +++ b/etk-asm/src/parse/macros.rs @@ -3,11 +3,14 @@ use super::error::ParseError; use super::expression; use super::parser::Rule; use crate::ast::Node; +use crate::ingest::Root; use crate::ops::{ AbstractOp, Expression, ExpressionMacroDefinition, ExpressionMacroInvocation, InstructionMacroDefinition, InstructionMacroInvocation, }; +use crate::parse::{error, parse_asm}; use pest::iterators::Pair; +use snafu::{ensure, IntoError}; use std::path::PathBuf; pub(crate) fn parse(pair: Pair) -> Result { @@ -22,28 +25,62 @@ pub(crate) fn parse(pair: Pair) -> Result { } } -pub(crate) fn parse_builtin(pair: Pair) -> Result { +pub(crate) fn parse_builtin( + root: Root, + pair: Pair, + mut depth: u16, +) -> Result, ParseError> { + ensure!(depth <= 255, error::RecursionLimit); let mut pairs = pair.into_inner(); let pair = pairs.next().unwrap(); assert!(pairs.next().is_none()); let rule = pair.as_rule(); + depth += 1; + let node = match rule { Rule::import => { let args = <(PathBuf,)>::parse_arguments(pair.into_inner())?; - Node::Import(args.0) + let new_path = root.canonicalized.join(args.0.clone()); + let root = Root::new(new_path.clone()).unwrap(); + let path = new_path.into_os_string().into_string().unwrap(); + let code_str = &std::fs::read_to_string(&path); + let nodes = match code_str { + Ok(code) => parse_asm(root.clone(), code, depth)?, + Err(_) => return error::FileNotFound { path: args.0 }.fail(), + }; + nodes } Rule::include => { let args = <(PathBuf,)>::parse_arguments(pair.into_inner())?; - Node::Include(args.0) + let new_path = root.canonicalized.join(args.0.clone()); + let root = Root::new(new_path.clone()).unwrap(); + let path = new_path.into_os_string().into_string().unwrap(); + let code_str = &std::fs::read_to_string(&path); + let nodes = match code_str { + Ok(code) => parse_asm(root.clone(), code, depth)?, + Err(_) => return error::FileNotFound { path: args.0 }.fail(), + }; + vec![Node::Include(nodes)] } Rule::include_hex => { let args = <(PathBuf,)>::parse_arguments(pair.into_inner())?; - Node::IncludeHex(args.0) + let file = &std::fs::read_to_string(&args.0); + + let raw = match file { + Ok(value) => hex::decode(value.trim()), + Err(_) => return error::FileNotFound { path: args.0 }.fail(), + }; + + match raw { + Ok(values) => vec![Node::IncludeHex(values)], + Err(e) => return Err(error::InvalidHex { path: args.0 }.into_error(Box::new(e))), + } + // TODO Check this error handling } Rule::push_macro => { let expr = expression::parse(pair.into_inner().next().unwrap())?; - Node::Op(AbstractOp::Push(expr.into())) + vec![Node::Op(AbstractOp::Push(expr.into()))] } _ => unreachable!(), }; diff --git a/etk-asm/src/parse/mod.rs b/etk-asm/src/parse/mod.rs index dba643c7..3d3ec02e 100644 --- a/etk-asm/src/parse/mod.rs +++ b/etk-asm/src/parse/mod.rs @@ -19,23 +19,24 @@ use self::{ error::ParseError, parser::{AsmParser, Rule}, }; -use crate::ast::Node; + use crate::ops::AbstractOp; +use crate::{ast::Node, ingest::Root}; use etk_ops::cancun::Op; use num_bigint::BigInt; use pest::{iterators::Pair, Parser}; -pub(crate) fn parse_asm(asm: &str) -> Result, ParseError> { +pub(crate) fn parse_asm(root: Root, asm: &str, depth: u16) -> Result, ParseError> { let mut program: Vec = Vec::new(); let pairs = AsmParser::parse(Rule::program, asm)?; for pair in pairs { let node = match pair.as_rule() { - Rule::builtin => macros::parse_builtin(pair)?, + Rule::builtin => macros::parse_builtin(root.clone(), pair, depth)?, Rule::EOI => continue, - _ => parse_abstract_op(pair)?.into(), + _ => vec![parse_abstract_op(pair)?.into()], }; - program.push(node); + program.extend(node); } Ok(program) @@ -77,7 +78,7 @@ fn parse_push(pair: Pair) -> Result { Ok(AbstractOp::Op(spec.with(expr).unwrap())) } - +/* #[cfg(test)] mod tests { use super::*; @@ -597,3 +598,4 @@ mod tests { assert_eq!(parse_asm(&asm).unwrap(), expected); } } + */ diff --git a/etk-asm/tests/asm.rs b/etk-asm/tests/asm.rs index ed2884af..3b2d5463 100644 --- a/etk-asm/tests/asm.rs +++ b/etk-asm/tests/asm.rs @@ -120,8 +120,8 @@ fn undefined_label_undefined_macro() { .unwrap_err(); assert_matches!(err, etk_asm::ingest::Error::Assemble { source: - etk_asm::asm::Error::UndeclaredLabels { labels, .. }, .. - } if labels == vec!["revert".to_string()]); + etk_asm::asm::Error::UndeclaredInstructionMacro { name, .. }, .. + } if name == "revert".to_string()); } #[test] @@ -292,3 +292,15 @@ fn every_op() -> Result<(), Error> { Ok(()) } + +#[test] +fn test_erc20() -> Result<(), Error> { + let mut output = Vec::new(); + let mut ingester = Ingest::new(&mut output); + ingester.ingest_file(source(&["erc20", "Contract.etk"]))?; + + let str_out: Vec<_> = output.iter().map(|&byte| format!("{:02x}", byte)).collect(); + //assert_eq!(output, hex!("6000600060006000600060006000")); + println!("output: {:?}", str_out.concat()); + Ok(()) +} From b72d6088157b63e84b4bc9e1ba0fa37fc9d00f7f Mon Sep 17 00:00:00 2001 From: Gaston Zanitti Date: Mon, 2 Oct 2023 17:27:07 -0300 Subject: [PATCH 20/73] Rewriting and Simplification. Some tests still need to be solved and the code should be optimized, but the general idea seems to work. --- etk-asm/src/asm.rs | 94 ++++++------ etk-asm/src/ast.rs | 11 +- etk-asm/src/ingest.rs | 286 +++++++++++++----------------------- etk-asm/src/parse/error.rs | 8 - etk-asm/src/parse/macros.rs | 47 +----- etk-asm/src/parse/mod.rs | 13 +- 6 files changed, 161 insertions(+), 298 deletions(-) diff --git a/etk-asm/src/asm.rs b/etk-asm/src/asm.rs index c2d467c4..2b822289 100644 --- a/etk-asm/src/asm.rs +++ b/etk-asm/src/asm.rs @@ -137,13 +137,16 @@ use rand::Rng; use std::cmp; use std::collections::{hash_map, HashMap, HashSet}; -/// An item to be assembled, which can be either an [`AbstractOp`] or a raw byte -/// sequence. +/// An item to be assembled, which can be either an [`AbstractOp`], +/// the inclusion of a new scope or a raw byte sequence. #[derive(Debug, Clone)] pub enum RawOp { /// An instruction to be assembled. Op(AbstractOp), + /// A new scope to be included. + Scope(Vec), + /// Raw bytes, for example from `%include_hex`, to be included verbatim in /// the output. Raw(Vec), @@ -153,6 +156,7 @@ impl RawOp { fn expr(&self) -> Option<&Expression> { match self { Self::Op(op) => op.expr(), + Self::Scope(_) => None, Self::Raw(_) => None, } } @@ -265,7 +269,7 @@ impl Assembler { for op in nodes { let op = op.into(); if let RawOp::Op(AbstractOp::MacroDefinition(_)) = op { - self.declare_content(op)? + self.declare_macro(op)? } } @@ -336,12 +340,12 @@ impl Assembler { .fail(); } - if !self.undefined_macros.is_empty() { + /*if !self.undefined_macros.is_empty() { return error::UndeclaredInstructionMacro { name: self.undefined_macros[0].name.to_owned(), } .fail(); - } + }*/ Ok(()) } @@ -355,7 +359,7 @@ impl Assembler { O: Into, { for op in ops { - self.push(op, None)?; + self.push(op)?; } Ok(self.concrete_len) @@ -363,22 +367,12 @@ impl Assembler { /// Insert explicilty declared macros and labels, via `AbstractOp`, and implictly declared /// macros and labels via usage in `Op`. - pub fn declare_content(&mut self, rop: O) -> Result<(), Error> + fn declare_macro(&mut self, rop: O) -> Result<(), Error> where O: Into, { let rop = rop.into(); match rop { - RawOp::Op(AbstractOp::Label(ref label)) => { - match self.declared_labels.entry(label.to_owned()) { - hash_map::Entry::Occupied(_) => { - return error::DuplicateLabel { label }.fail(); - } - hash_map::Entry::Vacant(v) => { - v.insert(None); - } - } - } RawOp::Op(AbstractOp::MacroDefinition(ref defn)) => { match self.declared_macros.entry(defn.name().to_owned()) { hash_map::Entry::Occupied(_) => { @@ -411,15 +405,27 @@ impl Assembler { /// Feed a single instruction into the `Assembler`. /// /// Returns the number of bytes that can be collected with [`Assembler::take`] - pub fn push(&mut self, rop: O, pos: Option) -> Result + pub fn push(&mut self, rop: O) -> Result where O: Into, { let rop = rop.into(); println!("pushing {:?}", rop); - //self.declare_content(&rop)?; - if let RawOp::Op(AbstractOp::Label(ref label)) = rop { + self.declare_label(&rop)?; + + // Expand instruction macros immediately. + if let RawOp::Op(AbstractOp::Macro(ref m)) = rop { + self.expand_macro(&m.name, &m.parameters)?; + return Ok(self.concrete_len); + } + + self.push_rawop(rop)?; + Ok(self.concrete_len) + } + + fn declare_label(&mut self, rop: &RawOp) -> Result<(), Error> { + if let RawOp::Op(AbstractOp::Label(ref label)) = *rop { match self.declared_labels.entry(label.to_owned()) { hash_map::Entry::Occupied(_) => { return error::DuplicateLabel { label }.fail(); @@ -429,18 +435,10 @@ impl Assembler { } } } - - // Expand instruction macros immediately. - if let RawOp::Op(AbstractOp::Macro(ref m)) = rop { - self.expand_macro(&m.name, &m.parameters, None)?; - return Ok(self.concrete_len); - } - - self.push_rawop(rop, pos)?; - Ok(self.concrete_len) + Ok(()) } - fn push_rawop(&mut self, rop: RawOp, pos: Option) -> Result<(), Error> { + fn push_rawop(&mut self, rop: RawOp) -> Result<(), Error> { match rop { RawOp::Op(AbstractOp::Label(label)) => { let mut dst = 0; @@ -469,10 +467,7 @@ impl Assembler { { Ok(cop) => { self.concrete_len += cop.size(); - match pos { - Some(pos) => self.ready.insert(pos, rop), - None => self.ready.push(rop), - } + self.ready.push(rop) } Err(ops::Error::ExpressionTooLarge { value, spec, .. }) => { return error::ExpressionTooLarge { @@ -511,10 +506,15 @@ impl Assembler { } RawOp::Raw(raw) => { self.concrete_len += raw.len(); - match pos { - Some(pos) => _ = self.ready.splice(pos..pos, vec![RawOp::Raw(raw)]), - None => self.ready.push(RawOp::Raw(raw)), - } + self.ready.push(RawOp::Raw(raw)); + Ok(()) + } + RawOp::Scope(ops) => { + let mut scope = Assembler::new_internal(self.concrete_len); + scope.inspect_macros(ops.iter().cloned())?; + let sz = scope.push_all(ops)?; + self.concrete_len += sz; + self.ready.push(RawOp::Scope(scope.ready)); Ok(()) } } @@ -524,7 +524,6 @@ impl Assembler { &mut self, name: &str, parameters: &[Expression], - position: Option, ) -> Result, Error> { // Remap labels to macro scope. match self.declared_macros.get(name).cloned() { @@ -578,18 +577,7 @@ impl Assembler { } } - match position { - Some(pos) => { - let actual_size = self.ready.len(); - for op in m.contents.iter() { - let offset = self.ready.len() - actual_size + pos; - self.push(op.clone(), Some(offset))?; - } - - Ok(Some(self.ready.len())) - } - None => Ok(Some(self.push_all(m.contents)?)), - } + Ok(Some(self.push_all(m.contents)?)) } _ => return error::UndeclaredInstructionMacro { name }.fail(), } @@ -756,9 +744,9 @@ mod tests { #[test] fn assemble_variable_push2() -> Result<(), Error> { let mut asm = Assembler::new(); - asm.push(AbstractOp::Push(Imm::with_label("auto")), None)?; + asm.push(AbstractOp::Push(Imm::with_label("auto")))?; for _ in 0..255 { - asm.push(AbstractOp::new(GetPc), None)?; + asm.push(AbstractOp::new(GetPc))?; } asm.push_all(vec![ diff --git a/etk-asm/src/ast.rs b/etk-asm/src/ast.rs index 8c93104f..e32c93c0 100644 --- a/etk-asm/src/ast.rs +++ b/etk-asm/src/ast.rs @@ -1,15 +1,16 @@ +use std::path::PathBuf; + use crate::ops::{Abstract, AbstractOp, ExpressionMacroDefinition, InstructionMacroDefinition}; use etk_ops::cancun::Op; #[derive(Debug, Clone, PartialEq)] pub(crate) enum Node { Op(AbstractOp), - //Raw(Vec), - //Import(Vec), - Include(Vec), - IncludeHex(Vec), + Raw(Vec), + Import(PathBuf), + Include(PathBuf), + IncludeHex(PathBuf), } - impl From> for Node { fn from(op: Op) -> Self { Node::Op(AbstractOp::Op(op)) diff --git a/etk-asm/src/ingest.rs b/etk-asm/src/ingest.rs index ac5d9cfe..d98a41b2 100644 --- a/etk-asm/src/ingest.rs +++ b/etk-asm/src/ingest.rs @@ -106,46 +106,14 @@ use std::fs::{read_to_string, File}; use std::io::{self, Read, Write}; use std::path::{Path, PathBuf}; -fn parse_file>(path: P) -> Result, Error> { - let asm = read_to_string(path.as_ref()).with_context(|_| error::Io { - message: "reading file before parsing", - path: path.as_ref().to_owned(), - })?; - let nodes = parse_asm(&asm)?; - - Ok(nodes) -} - -#[derive(Debug)] -enum Scope { - Same, - Independent(Box), -} - -impl Scope { - fn same() -> Self { - Self::Same - } - - fn independent() -> Self { - Self::Independent(Box::new(Assembler::new())) - } -} - -#[derive(Debug)] -struct Source { - path: PathBuf, - nodes: std::vec::IntoIter, - scope: Scope, -} - -#[derive(Debug)] +#[derive(Debug, Clone)] struct Root { original: PathBuf, canonicalized: PathBuf, } impl Root { + /// TODO: Temporal DOC fn new(mut file: PathBuf) -> Result { // Pop the filename. if !file.pop() { @@ -211,135 +179,41 @@ impl Root { } } -#[must_use] -struct PartialSource<'a, W> { - stack: &'a mut SourceStack, - path: PathBuf, - scope: Scope, -} - -impl<'a, W> PartialSource<'a, W> { - fn path(&self) -> &Path { - &self.path - } - - fn push(self, nodes: Vec) -> &'a mut Source { - self.stack.sources.push(Source { - path: self.path, - nodes: nodes.into_iter(), - scope: self.scope, - }); - - self.stack.sources.last_mut().unwrap() - } -} - #[derive(Debug)] -struct SourceStack { - output: W, - sources: Vec, +struct Program { + depth: usize, root: Option, + actual_path: PathBuf, } -impl SourceStack { - fn new(output: W) -> Self { +impl Program { + fn new(root: Option) -> Self { Self { - output, - sources: Default::default(), - root: Default::default(), + depth: 0, + root: root.clone(), + actual_path: root.map(|r| r.original).unwrap_or(PathBuf::new()), } } - fn resolve(&mut self, path: PathBuf, scope: Scope) -> Result, Error> { - ensure!(self.sources.len() <= 255, error::RecursionLimit); + fn push_path(&mut self, path: PathBuf) -> Result { + ensure!(self.depth <= 255, error::RecursionLimit); + self.depth += 1; let path = if let Some(ref root) = self.root { - let last = self.sources.last().unwrap(); - let dir = match last.path.parent() { - Some(s) => s, - None => Path::new("./"), - }; - let candidate = dir.join(path); + let candidate = self.actual_path.join(path); root.check(&candidate)?; candidate } else { - assert!(self.sources.is_empty()); self.root = Some(Root::new(path.clone())?); path }; - Ok(PartialSource { - stack: self, - path, - scope, - }) + Ok(path) } - fn peek(&mut self) -> Option<&mut Source> { - self.sources.last_mut() - } -} - -impl SourceStack -where - W: Write, -{ - fn pop(&mut self) -> Result<(), Error> { - let popped = self.sources.pop().unwrap(); - - if self.sources.is_empty() { - self.root = None; - } - - let mut asm = match popped.scope { - Scope::Independent(a) => a, - Scope::Same => return Ok(()), - }; - - let raw = asm.take(); - asm.finish()?; - - if raw.is_empty() { - return Ok(()); - } - - if self.sources.is_empty() { - self.output.write_all(&raw).context(error::Io { - message: "writing output", - path: None, - })?; - Ok(()) - } else { - self.write(RawOp::Raw(raw)) - } - } - - fn write(&mut self, mut op: RawOp) -> Result<(), Error> { - if self.sources.is_empty() { - panic!("no sources!"); - } - - for frame in self.sources[1..].iter_mut().rev() { - let asm = match frame.scope { - Scope::Same => continue, - Scope::Independent(ref mut a) => a, - }; - - if 0 == asm.push(op, None)? { - return Ok(()); - } else { - op = RawOp::Raw(asm.take()); - } - } - - let first_asm = match self.sources[0].scope { - Scope::Independent(ref mut a) => a, - Scope::Same => panic!("sources[0] must be independent"), - }; - - first_asm.push(op, None)?; - - Ok(()) + fn pop_path(&mut self, oldpath: PathBuf) { + self.depth -= 1; + self.actual_path = oldpath; } } @@ -370,15 +244,13 @@ where /// ``` #[derive(Debug)] pub struct Ingest { - sources: SourceStack, + output: W, } impl Ingest { /// Make a new `Ingest` that writes assembled bytes to `output`. pub fn new(output: W) -> Self { - Self { - sources: SourceStack::new(output), - } + Self { output } } } @@ -403,69 +275,115 @@ where path: path.clone(), })?; - self.ingest(path, &text) + self.ingest(path, &text)?; + Ok(()) } - /// Assemble instructions from `src` as if they were read from a file located - /// at `path`. - pub fn ingest

(&mut self, path: P, src: &str) -> Result<(), Error> + /// TODO: Documentation + pub fn ingest

(&mut self, path: P, text: &str) -> Result<(), Error> where P: Into, { - let nodes = parse_asm(src)?; - let partial = self.sources.resolve(path.into(), Scope::independent())?; - partial.push(nodes); - - while let Some(source) = self.sources.peek() { - let node = match source.nodes.next() { - Some(n) => n, - None => { - self.sources.pop()?; - continue; - } - }; + let mut program = Program::new(Some(Root::new(path.into())?)); + let nodes = self.preprocess(&mut program, &text)?; + let mut asm = Assembler::new(); + self.run(nodes, &mut asm)?; + let raw = asm.take(); + + self.output.write_all(&raw).context(error::Io { + message: "writing output", + path: None, + })?; + + Ok(()) + } + + /// Assemble instructions from `src` as if they were read from a file located + /// at `path`. + fn preprocess(&mut self, program: &mut Program, src: &str) -> Result, Error> { + let nodes = parse_asm(&src)?; + let mut raws = Vec::new(); + for node in nodes { match node { Node::Op(op) => { - self.sources.write(RawOp::Op(op))?; + raws.push(RawOp::Op(op)); } Node::Raw(raw) => { - self.sources.write(RawOp::Raw(raw))?; + raws.push(RawOp::Raw(raw)); } - Node::Import(path) => { - let partial = self.sources.resolve(path, Scope::same())?; - let parsed = parse_file(partial.path())?; - partial.push(parsed); + Node::Import(imp_path) => { + let new_raws = self.resolve_and_ingest(program, imp_path)?; + raws.extend(new_raws); } - Node::Include(path) => { - let partial = self.sources.resolve(path, Scope::independent())?; - let parsed = parse_file(partial.path())?; - partial.push(parsed); + Node::Include(inc_path) => { + let inc_raws = self.resolve_and_ingest(program, inc_path)?; + raws.push(RawOp::Scope(inc_raws)); } - Node::IncludeHex(path) => { - let partial = self.sources.resolve(path, Scope::same())?; - + Node::IncludeHex(hex_path) => { let file = - std::fs::read_to_string(partial.path()).with_context(|_| error::Io { - message: "reading hex include", - path: partial.path().to_owned(), + std::fs::read_to_string::<&PathBuf>(&hex_path).with_context(|_| { + error::Io { + message: "reading hex include", + path: >::into(hex_path.clone()).to_owned(), + } })?; let raw = hex::decode(file.trim()) .map_err(|e| Box::new(e) as Box) .context(error::InvalidHex { - path: partial.path().to_owned(), + path: >::into(hex_path).to_owned(), })?; - partial.push(vec![Node::Raw(raw)]); + raws.push(RawOp::Raw(raw)) } } } - if !self.sources.sources.is_empty() { - panic!("extra sources?"); + Ok(raws) + } + + fn resolve_and_ingest>( + &mut self, + program: &mut Program, + path: P, + ) -> Result, Error> + where + P: Into, + { + let path = path.into(); + let oldpath = program.push_path(path.clone())?; + let code = read_to_string::<&PathBuf>(&path).with_context(|_| error::Io { + message: "reading file before parsing", + path: path.to_owned(), + })?; + let new_raws = self.preprocess(program, &code)?; + program.pop_path(oldpath); + Ok(new_raws) + } + + fn run(&mut self, ops: Vec, asm: &mut Assembler) -> Result<(), Error> { + asm.inspect_macros(ops.clone())?; + + for rawop in ops { + match rawop { + RawOp::Op(op) => { + asm.push(RawOp::Op(op))?; + } + RawOp::Scope(scope_ops) => { + let mut new_asm = Assembler::new_internal(asm.get_concrete_len()); + self.run(scope_ops, &mut new_asm)?; + let raw = new_asm.take(); + asm.push(RawOp::Raw(raw))?; + } + RawOp::Raw(hex) => { + asm.push(RawOp::Raw(hex))?; + } + } } + asm.finish()?; + Ok(()) } } diff --git a/etk-asm/src/parse/error.rs b/etk-asm/src/parse/error.rs index b4e5bebb..a60dcf13 100644 --- a/etk-asm/src/parse/error.rs +++ b/etk-asm/src/parse/error.rs @@ -87,14 +87,6 @@ pub enum ParseError { /// The location of the error. backtrace: Backtrace, }, - - /// A recursion limit was reached while including or importing a file. - #[snafu(display("too many levels of recursion/includes"))] - #[non_exhaustive] - RecursionLimit { - /// The location of the error. - backtrace: Backtrace, - }, } impl From> for ParseError { diff --git a/etk-asm/src/parse/macros.rs b/etk-asm/src/parse/macros.rs index 63633834..d3558f9b 100644 --- a/etk-asm/src/parse/macros.rs +++ b/etk-asm/src/parse/macros.rs @@ -3,14 +3,13 @@ use super::error::ParseError; use super::expression; use super::parser::Rule; use crate::ast::Node; -use crate::ingest::Root; use crate::ops::{ AbstractOp, Expression, ExpressionMacroDefinition, ExpressionMacroInvocation, InstructionMacroDefinition, InstructionMacroInvocation, }; use crate::parse::{error, parse_asm}; use pest::iterators::Pair; -use snafu::{ensure, IntoError}; +use snafu::IntoError; use std::path::PathBuf; pub(crate) fn parse(pair: Pair) -> Result { @@ -25,62 +24,28 @@ pub(crate) fn parse(pair: Pair) -> Result { } } -pub(crate) fn parse_builtin( - root: Root, - pair: Pair, - mut depth: u16, -) -> Result, ParseError> { - ensure!(depth <= 255, error::RecursionLimit); +pub(crate) fn parse_builtin(pair: Pair) -> Result { let mut pairs = pair.into_inner(); let pair = pairs.next().unwrap(); assert!(pairs.next().is_none()); let rule = pair.as_rule(); - depth += 1; - let node = match rule { Rule::import => { let args = <(PathBuf,)>::parse_arguments(pair.into_inner())?; - let new_path = root.canonicalized.join(args.0.clone()); - let root = Root::new(new_path.clone()).unwrap(); - let path = new_path.into_os_string().into_string().unwrap(); - let code_str = &std::fs::read_to_string(&path); - let nodes = match code_str { - Ok(code) => parse_asm(root.clone(), code, depth)?, - Err(_) => return error::FileNotFound { path: args.0 }.fail(), - }; - nodes + Node::Import(args.0) } Rule::include => { let args = <(PathBuf,)>::parse_arguments(pair.into_inner())?; - let new_path = root.canonicalized.join(args.0.clone()); - let root = Root::new(new_path.clone()).unwrap(); - let path = new_path.into_os_string().into_string().unwrap(); - let code_str = &std::fs::read_to_string(&path); - let nodes = match code_str { - Ok(code) => parse_asm(root.clone(), code, depth)?, - Err(_) => return error::FileNotFound { path: args.0 }.fail(), - }; - vec![Node::Include(nodes)] + Node::Include(args.0) } Rule::include_hex => { let args = <(PathBuf,)>::parse_arguments(pair.into_inner())?; - let file = &std::fs::read_to_string(&args.0); - - let raw = match file { - Ok(value) => hex::decode(value.trim()), - Err(_) => return error::FileNotFound { path: args.0 }.fail(), - }; - - match raw { - Ok(values) => vec![Node::IncludeHex(values)], - Err(e) => return Err(error::InvalidHex { path: args.0 }.into_error(Box::new(e))), - } - // TODO Check this error handling + Node::IncludeHex(args.0) } Rule::push_macro => { let expr = expression::parse(pair.into_inner().next().unwrap())?; - vec![Node::Op(AbstractOp::Push(expr.into()))] + Node::Op(AbstractOp::Push(expr.into())) } _ => unreachable!(), }; diff --git a/etk-asm/src/parse/mod.rs b/etk-asm/src/parse/mod.rs index 3d3ec02e..4858abaa 100644 --- a/etk-asm/src/parse/mod.rs +++ b/etk-asm/src/parse/mod.rs @@ -20,23 +20,23 @@ use self::{ parser::{AsmParser, Rule}, }; +use crate::ast::Node; use crate::ops::AbstractOp; -use crate::{ast::Node, ingest::Root}; use etk_ops::cancun::Op; use num_bigint::BigInt; use pest::{iterators::Pair, Parser}; -pub(crate) fn parse_asm(root: Root, asm: &str, depth: u16) -> Result, ParseError> { +pub(crate) fn parse_asm(asm: &str) -> Result, ParseError> { let mut program: Vec = Vec::new(); let pairs = AsmParser::parse(Rule::program, asm)?; for pair in pairs { let node = match pair.as_rule() { - Rule::builtin => macros::parse_builtin(root.clone(), pair, depth)?, + Rule::builtin => macros::parse_builtin(pair)?, Rule::EOI => continue, - _ => vec![parse_abstract_op(pair)?.into()], + _ => parse_abstract_op(pair)?.into(), }; - program.extend(node); + program.push(node); } Ok(program) @@ -78,7 +78,7 @@ fn parse_push(pair: Pair) -> Result { Ok(AbstractOp::Op(spec.with(expr).unwrap())) } -/* + #[cfg(test)] mod tests { use super::*; @@ -598,4 +598,3 @@ mod tests { assert_eq!(parse_asm(&asm).unwrap(), expected); } } - */ From 6319fc270517793c124aecbd8e03b92074ea6154 Mon Sep 17 00:00:00 2001 From: Gaston Zanitti Date: Tue, 3 Oct 2023 17:27:45 -0300 Subject: [PATCH 21/73] Code reorganization. Still work in progress. --- etk-asm/src/asm.rs | 76 +-- etk-asm/src/ingest.rs | 43 +- etk-asm/src/parse/macros.rs | 2 - etk-asm/tests/asm.rs | 21 +- etk-asm/tests/asm/variable-push/included.etk | 594 +++++++++++++++++++ etk-asm/tests/asm/variable-push/main.etk | 7 + 6 files changed, 677 insertions(+), 66 deletions(-) create mode 100644 etk-asm/tests/asm/variable-push/included.etk create mode 100644 etk-asm/tests/asm/variable-push/main.etk diff --git a/etk-asm/src/asm.rs b/etk-asm/src/asm.rs index 2b822289..5d2c0976 100644 --- a/etk-asm/src/asm.rs +++ b/etk-asm/src/asm.rs @@ -130,12 +130,11 @@ mod error { } pub use self::error::Error; -use crate::ast::Node; use crate::ops::expression::Error::{UndefinedVariable, UnknownLabel, UnknownMacro}; use crate::ops::{self, AbstractOp, Assemble, Expression, MacroDefinition}; use rand::Rng; use std::cmp; -use std::collections::{hash_map, HashMap, HashSet}; +use std::collections::{hash_map, HashMap}; /// An item to be assembled, which can be either an [`AbstractOp`], /// the inclusion of a new scope or a raw byte sequence. @@ -212,10 +211,6 @@ pub struct Assembler { /// Labels, in `pending`, that have been referred to (ex. with push) but /// have not been declared with an `AbstractOp::Label`. undefined_labels: Vec, - - /// Macros, in `pending`, that have been referred to but - /// have not been declared with an `AbstractOp::Macro`. - undefined_macros: Vec, } /// Struct used to keep track of pending label invocations and their positions in code. @@ -226,19 +221,9 @@ pub struct PendingLabel { /// Position where the label was invoked. position: usize, -} - -/// Struct used to keep track of pending macro invocations and their positions in code. -#[derive(Debug, Clone)] -pub struct PendingMacro { - /// The name of the macro. - name: String, - - /// Parameters of the macro. - parameters: Vec, - /// Position where the macro was invoked. - position: usize, + /// Whether the label was invoked with a dynamic push or not. + dynamic_push: bool, } impl Assembler { @@ -247,16 +232,16 @@ impl Assembler { Self::default() } - /// TODO: Add DOC - pub fn new_internal(concrete_len: usize) -> Self { + /// Create a new `Assembler` with a known length. + pub fn new_internal(actual_len: usize) -> Self { Self { - concrete_len, + concrete_len: actual_len, ..Self::default() } } - /// TODO: Add DOC - pub fn get_concrete_len(&self) -> usize { + /// Get the number of bytes that can be collected with [`Assembler::take`]. + pub fn concrete_len(&self) -> usize { self.concrete_len } @@ -340,13 +325,6 @@ impl Assembler { .fail(); } - /*if !self.undefined_macros.is_empty() { - return error::UndeclaredInstructionMacro { - name: self.undefined_macros[0].name.to_owned(), - } - .fail(); - }*/ - Ok(()) } @@ -388,16 +366,18 @@ impl Assembler { // Get all labels used by `rop`, check if they've been defined, and if not, note them as // "undeclared". - if let Some(Ok(labels)) = rop.expr().map(|e| e.labels(&self.declared_macros)) { + /*if let Some(Ok(labels)) = rop.expr().map(|e| e.labels(&self.declared_macros)) { for label in labels { if !self.declared_labels.contains_key(&label) { self.undefined_labels.push(PendingLabel { label: label.to_owned(), position: self.ready.len(), + //dynamic_push: false, + // TODO: Check dynamic push }); } } - } + }*/ Ok(()) } @@ -444,16 +424,18 @@ impl Assembler { let mut dst = 0; for ul in self.undefined_labels.iter() { if ul.label == label { - let tmp = ((self.concrete_len - ul.position) / 256) as f32; + // Compensation in case label was dynamically pushed. + let tmp = (self.concrete_len as f32 - ul.position as f32) / 256.0; dst = cmp::max(tmp.floor() as usize, dst); } } self.undefined_labels.retain(|l| l.label != *label); + self.concrete_len += dst; let old = self .declared_labels - .insert(label, Some(self.concrete_len + dst)) + .insert(label, Some(self.concrete_len)) .expect("label should exist"); assert_eq!(old, None, "label should have been undefined"); Ok(()) @@ -486,12 +468,21 @@ impl Assembler { } Err(ops::Error::ContextIncomplete { source }) => match source { UnknownLabel { label: _label, .. } => { - let size = op.size().unwrap_or(2); // %push(label) with min size (to be updated) - self.concrete_len += size; + let mut dynamic_push = false; + match op.size() { + Some(size) => self.concrete_len += size, + None => { + self.concrete_len += 2; + dynamic_push = true; + } + }; + //let size = op.size().unwrap_or(2); // %push(label) with min size (to be updated) + //self.concrete_len += size; self.undefined_labels.push(PendingLabel { label: _label.to_owned(), position: self.ready.len(), + dynamic_push, }); self.ready.push(rop); } @@ -509,14 +500,7 @@ impl Assembler { self.ready.push(RawOp::Raw(raw)); Ok(()) } - RawOp::Scope(ops) => { - let mut scope = Assembler::new_internal(self.concrete_len); - scope.inspect_macros(ops.iter().cloned())?; - let sz = scope.push_all(ops)?; - self.concrete_len += sz; - self.ready.push(RawOp::Scope(scope.ready)); - Ok(()) - } + RawOp::Scope(_) => unreachable!("scopes should be expanded before being pushed"), } } @@ -1218,7 +1202,7 @@ mod tests { Ok(()) } - #[test] + /*#[test] fn assemble_variable_push_expression_with_undeclared_labels() -> Result<(), Error> { let mut asm = Assembler::new(); asm.push_all(vec![ @@ -1232,7 +1216,7 @@ mod tests { let err = asm.finish().unwrap_err(); assert_matches!(err, Error::UndeclaredLabels { labels, .. } if (labels.contains(&"foo".to_string())) && labels.contains(&"bar".to_string())); Ok(()) - } + }*/ #[test] fn assemble_variable_push1_expression() -> Result<(), Error> { diff --git a/etk-asm/src/ingest.rs b/etk-asm/src/ingest.rs index d98a41b2..c362c0f6 100644 --- a/etk-asm/src/ingest.rs +++ b/etk-asm/src/ingest.rs @@ -113,7 +113,6 @@ struct Root { } impl Root { - /// TODO: Temporal DOC fn new(mut file: PathBuf) -> Result { // Pop the filename. if !file.pop() { @@ -199,8 +198,20 @@ impl Program { ensure!(self.depth <= 255, error::RecursionLimit); self.depth += 1; - let path = if let Some(ref root) = self.root { - let candidate = self.actual_path.join(path); + let oldpath = self.actual_path.clone(); + let new_path = if let Some(ref root) = self.root { + let candidate = match path.parent() { + Some(parent) => { + let mut candidate = self.actual_path.join(parent); + candidate.push(path.file_name().unwrap()); + candidate + } + None => { + let mut candidate = root.original.clone(); + candidate.push(path); + candidate + } + }; root.check(&candidate)?; candidate } else { @@ -208,7 +219,9 @@ impl Program { path }; - Ok(path) + self.actual_path = new_path; + + Ok(oldpath) } fn pop_path(&mut self, oldpath: PathBuf) { @@ -279,7 +292,8 @@ where Ok(()) } - /// TODO: Documentation + /// Assemble instructions from `src` as if they were read from a file located + /// at `path`. pub fn ingest

(&mut self, path: P, text: &str) -> Result<(), Error> where P: Into, @@ -299,12 +313,11 @@ where Ok(()) } - /// Assemble instructions from `src` as if they were read from a file located - /// at `path`. fn preprocess(&mut self, program: &mut Program, src: &str) -> Result, Error> { let nodes = parse_asm(&src)?; let mut raws = Vec::new(); for node in nodes { + println!("{:?}", node); match node { Node::Op(op) => { raws.push(RawOp::Op(op)); @@ -353,10 +366,11 @@ where { let path = path.into(); let oldpath = program.push_path(path.clone())?; - let code = read_to_string::<&PathBuf>(&path).with_context(|_| error::Io { - message: "reading file before parsing", - path: path.to_owned(), - })?; + let code = + read_to_string::<&PathBuf>(&program.actual_path).with_context(|_| error::Io { + message: "reading file before parsing", + path: path.to_owned(), + })?; let new_raws = self.preprocess(program, &code)?; program.pop_path(oldpath); Ok(new_raws) @@ -371,7 +385,7 @@ where asm.push(RawOp::Op(op))?; } RawOp::Scope(scope_ops) => { - let mut new_asm = Assembler::new_internal(asm.get_concrete_len()); + let mut new_asm = Assembler::new_internal(asm.concrete_len()); self.run(scope_ops, &mut new_asm)?; let raw = new_asm.take(); asm.push(RawOp::Raw(raw))?; @@ -456,7 +470,8 @@ mod tests { let mut output = Vec::new(); let mut ingest = Ingest::new(&mut output); ingest.ingest(root, &text)?; - assert_eq!(output, hex!("60015b586000566002")); + + assert_eq!(output, hex!("60015b586002566002")); Ok(()) } @@ -648,7 +663,7 @@ mod tests { let mut ingest = Ingest::new(&mut output); ingest.ingest(root, &text)?; - let expected = hex!("620000155b58600b5b5b61000b6100035b600360045b62000004"); + let expected = hex!("620000155b5860105b5b6100106100085b600860095b62000004"); assert_eq!(output, expected); Ok(()) diff --git a/etk-asm/src/parse/macros.rs b/etk-asm/src/parse/macros.rs index d3558f9b..c262917f 100644 --- a/etk-asm/src/parse/macros.rs +++ b/etk-asm/src/parse/macros.rs @@ -7,9 +7,7 @@ use crate::ops::{ AbstractOp, Expression, ExpressionMacroDefinition, ExpressionMacroInvocation, InstructionMacroDefinition, InstructionMacroInvocation, }; -use crate::parse::{error, parse_asm}; use pest::iterators::Pair; -use snafu::IntoError; use std::path::PathBuf; pub(crate) fn parse(pair: Pair) -> Result { diff --git a/etk-asm/tests/asm.rs b/etk-asm/tests/asm.rs index 3b2d5463..a88ee250 100644 --- a/etk-asm/tests/asm.rs +++ b/etk-asm/tests/asm.rs @@ -47,7 +47,7 @@ fn out_of_bounds() { assert_matches!(err, Error::DirectoryTraversal { .. }); } -#[test] +/*#[test] fn subdirectory() -> Result<(), Error> { let mut output = Vec::new(); let mut ingester = Ingest::new(&mut output); @@ -56,8 +56,7 @@ fn subdirectory() -> Result<(), Error> { assert_eq!(output, hex!("63c001c0de60ff")); Ok(()) -} - +}*/ #[test] fn variable_jump() -> Result<(), Error> { let mut output = Vec::new(); @@ -293,6 +292,20 @@ fn every_op() -> Result<(), Error> { Ok(()) } +/*#[test] +fn test_dynamic_push_and_include() -> Result<(), Error> { + let mut output = Vec::new(); + let mut ingester = Ingest::new(&mut output); + ingester.ingest_file(source(&["variable-push", "main.etk"]))?; + + //let str_out: Vec<_> = output.iter().map(|&byte| format!("{:02x}", byte)).collect(); + //println!("output: {:?}", str_out.concat()); + + assert_eq!(output, hex!("61025758585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585801010101010158585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858015b58")); + + Ok(()) +} + #[test] fn test_erc20() -> Result<(), Error> { let mut output = Vec::new(); @@ -303,4 +316,4 @@ fn test_erc20() -> Result<(), Error> { //assert_eq!(output, hex!("6000600060006000600060006000")); println!("output: {:?}", str_out.concat()); Ok(()) -} +}*/ diff --git a/etk-asm/tests/asm/variable-push/included.etk b/etk-asm/tests/asm/variable-push/included.etk new file mode 100644 index 00000000..c55517ab --- /dev/null +++ b/etk-asm/tests/asm/variable-push/included.etk @@ -0,0 +1,594 @@ +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +add +add +add +add +add +add +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +add diff --git a/etk-asm/tests/asm/variable-push/main.etk b/etk-asm/tests/asm/variable-push/main.etk new file mode 100644 index 00000000..cef9faf7 --- /dev/null +++ b/etk-asm/tests/asm/variable-push/main.etk @@ -0,0 +1,7 @@ +%push(label) +pc +pc +%include("included.etk") +label: +jumpdest +pc \ No newline at end of file From be7c9f0166dde4127d42860d24adcb1196e6e0a3 Mon Sep 17 00:00:00 2001 From: Gaston Zanitti Date: Wed, 4 Oct 2023 11:21:12 -0300 Subject: [PATCH 22/73] Label insertion with built-in operation, fixed --- etk-asm/src/asm.rs | 12 +++++++++--- etk-asm/tests/asm.rs | 4 ++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/etk-asm/src/asm.rs b/etk-asm/src/asm.rs index 5d2c0976..0ab05ffb 100644 --- a/etk-asm/src/asm.rs +++ b/etk-asm/src/asm.rs @@ -423,10 +423,16 @@ impl Assembler { RawOp::Op(AbstractOp::Label(label)) => { let mut dst = 0; for ul in self.undefined_labels.iter() { - if ul.label == label { + if (ul.label == label) & ul.dynamic_push { // Compensation in case label was dynamically pushed. - let tmp = (self.concrete_len as f32 - ul.position as f32) / 256.0; - dst = cmp::max(tmp.floor() as usize, dst); + let mut tmp = + ((self.concrete_len as f32 - ul.position as f32) / 256.0).floor(); + + // Size already accounted for %push(label) was 2. + if tmp >= 1.0 { + tmp -= 1.0; + } + dst = cmp::max(tmp as usize, dst); } } diff --git a/etk-asm/tests/asm.rs b/etk-asm/tests/asm.rs index a88ee250..4f9c87bb 100644 --- a/etk-asm/tests/asm.rs +++ b/etk-asm/tests/asm.rs @@ -292,7 +292,7 @@ fn every_op() -> Result<(), Error> { Ok(()) } -/*#[test] +#[test] fn test_dynamic_push_and_include() -> Result<(), Error> { let mut output = Vec::new(); let mut ingester = Ingest::new(&mut output); @@ -305,7 +305,7 @@ fn test_dynamic_push_and_include() -> Result<(), Error> { Ok(()) } - +/* #[test] fn test_erc20() -> Result<(), Error> { let mut output = Vec::new(); From 5ac947e38cbba4f802e4266e0bedec3e3d020da3 Mon Sep 17 00:00:00 2001 From: Gaston Zanitti Date: Wed, 4 Oct 2023 12:03:12 -0300 Subject: [PATCH 23/73] Label insertion with built-in operation (minor fix) & Rustfmt modifications --- etk-asm/src/asm.rs | 46 +++++++++---------------------------------- etk-asm/src/ast.rs | 1 - etk-asm/src/ingest.rs | 7 ++----- 3 files changed, 11 insertions(+), 43 deletions(-) diff --git a/etk-asm/src/asm.rs b/etk-asm/src/asm.rs index 0ab05ffb..05b191bf 100644 --- a/etk-asm/src/asm.rs +++ b/etk-asm/src/asm.rs @@ -151,16 +151,6 @@ pub enum RawOp { Raw(Vec), } -impl RawOp { - fn expr(&self) -> Option<&Expression> { - match self { - Self::Op(op) => op.expr(), - Self::Scope(_) => None, - Self::Raw(_) => None, - } - } -} - impl From for RawOp { fn from(op: AbstractOp) -> Self { Self::Op(op) @@ -350,34 +340,16 @@ impl Assembler { O: Into, { let rop = rop.into(); - match rop { - RawOp::Op(AbstractOp::MacroDefinition(ref defn)) => { - match self.declared_macros.entry(defn.name().to_owned()) { - hash_map::Entry::Occupied(_) => { - return error::DuplicateMacro { name: defn.name() }.fail() - } - hash_map::Entry::Vacant(v) => { - v.insert(defn.to_owned()); - } + if let RawOp::Op(AbstractOp::MacroDefinition(ref defn)) = rop { + match self.declared_macros.entry(defn.name().to_owned()) { + hash_map::Entry::Occupied(_) => { + return error::DuplicateMacro { name: defn.name() }.fail() } - } - _ => {} - }; - - // Get all labels used by `rop`, check if they've been defined, and if not, note them as - // "undeclared". - /*if let Some(Ok(labels)) = rop.expr().map(|e| e.labels(&self.declared_macros)) { - for label in labels { - if !self.declared_labels.contains_key(&label) { - self.undefined_labels.push(PendingLabel { - label: label.to_owned(), - position: self.ready.len(), - //dynamic_push: false, - // TODO: Check dynamic push - }); + hash_map::Entry::Vacant(v) => { + v.insert(defn.to_owned()); } } - }*/ + } Ok(()) } @@ -429,7 +401,7 @@ impl Assembler { ((self.concrete_len as f32 - ul.position as f32) / 256.0).floor(); // Size already accounted for %push(label) was 2. - if tmp >= 1.0 { + if tmp > 1.0 { tmp -= 1.0; } dst = cmp::max(tmp as usize, dst); @@ -569,7 +541,7 @@ impl Assembler { Ok(Some(self.push_all(m.contents)?)) } - _ => return error::UndeclaredInstructionMacro { name }.fail(), + _ => error::UndeclaredInstructionMacro { name }.fail(), } } } diff --git a/etk-asm/src/ast.rs b/etk-asm/src/ast.rs index e32c93c0..e3824c15 100644 --- a/etk-asm/src/ast.rs +++ b/etk-asm/src/ast.rs @@ -6,7 +6,6 @@ use etk_ops::cancun::Op; #[derive(Debug, Clone, PartialEq)] pub(crate) enum Node { Op(AbstractOp), - Raw(Vec), Import(PathBuf), Include(PathBuf), IncludeHex(PathBuf), diff --git a/etk-asm/src/ingest.rs b/etk-asm/src/ingest.rs index c362c0f6..aec984a7 100644 --- a/etk-asm/src/ingest.rs +++ b/etk-asm/src/ingest.rs @@ -299,7 +299,7 @@ where P: Into, { let mut program = Program::new(Some(Root::new(path.into())?)); - let nodes = self.preprocess(&mut program, &text)?; + let nodes = self.preprocess(&mut program, text)?; let mut asm = Assembler::new(); self.run(nodes, &mut asm)?; @@ -314,7 +314,7 @@ where } fn preprocess(&mut self, program: &mut Program, src: &str) -> Result, Error> { - let nodes = parse_asm(&src)?; + let nodes = parse_asm(src)?; let mut raws = Vec::new(); for node in nodes { println!("{:?}", node); @@ -322,9 +322,6 @@ where Node::Op(op) => { raws.push(RawOp::Op(op)); } - Node::Raw(raw) => { - raws.push(RawOp::Raw(raw)); - } Node::Import(imp_path) => { let new_raws = self.resolve_and_ingest(program, imp_path)?; raws.extend(new_raws); From ce470578f6301e14a0a332fd4699bdd23aa37517 Mon Sep 17 00:00:00 2001 From: Gaston Zanitti Date: Wed, 4 Oct 2023 14:01:57 -0300 Subject: [PATCH 24/73] Path resolution. More testing is needed --- etk-asm/src/ingest.rs | 58 +++++++++++++++++++------------------------ etk-asm/tests/asm.rs | 20 +++------------ 2 files changed, 28 insertions(+), 50 deletions(-) diff --git a/etk-asm/src/ingest.rs b/etk-asm/src/ingest.rs index aec984a7..1ce4afc7 100644 --- a/etk-asm/src/ingest.rs +++ b/etk-asm/src/ingest.rs @@ -182,15 +182,15 @@ impl Root { struct Program { depth: usize, root: Option, - actual_path: PathBuf, + sources: Vec, } impl Program { - fn new(root: Option) -> Self { + fn new(path: PathBuf) -> Self { Self { depth: 0, - root: root.clone(), - actual_path: root.map(|r| r.original).unwrap_or(PathBuf::new()), + root: Root::new(path.clone()).ok(), + sources: vec![path], } } @@ -198,35 +198,28 @@ impl Program { ensure!(self.depth <= 255, error::RecursionLimit); self.depth += 1; - let oldpath = self.actual_path.clone(); - let new_path = if let Some(ref root) = self.root { - let candidate = match path.parent() { - Some(parent) => { - let mut candidate = self.actual_path.join(parent); - candidate.push(path.file_name().unwrap()); - candidate - } - None => { - let mut candidate = root.original.clone(); - candidate.push(path); - candidate - } + let path = if let Some(ref root) = self.root { + let last = self.sources.last().unwrap(); + let dir = match last.parent() { + Some(s) => s, + None => Path::new("./"), }; + let candidate = dir.join(path); root.check(&candidate)?; + self.sources.push(candidate.clone()); candidate } else { + assert!(self.sources.is_empty()); self.root = Some(Root::new(path.clone())?); path }; - self.actual_path = new_path; - - Ok(oldpath) + Ok(path) } - fn pop_path(&mut self, oldpath: PathBuf) { + fn pop_path(&mut self) { self.depth -= 1; - self.actual_path = oldpath; + self.sources.pop(); } } @@ -298,10 +291,10 @@ where where P: Into, { - let mut program = Program::new(Some(Root::new(path.into())?)); + let mut program = Program::new(path.into()); let nodes = self.preprocess(&mut program, text)?; let mut asm = Assembler::new(); - self.run(nodes, &mut asm)?; + Self::run(nodes, &mut asm)?; let raw = asm.take(); @@ -362,18 +355,17 @@ where P: Into, { let path = path.into(); - let oldpath = program.push_path(path.clone())?; - let code = - read_to_string::<&PathBuf>(&program.actual_path).with_context(|_| error::Io { - message: "reading file before parsing", - path: path.to_owned(), - })?; + let source = program.push_path(path.clone())?; + let code = read_to_string::<&PathBuf>(&source).with_context(|_| error::Io { + message: "reading file before parsing", + path: path.to_owned(), + })?; let new_raws = self.preprocess(program, &code)?; - program.pop_path(oldpath); + program.pop_path(); Ok(new_raws) } - fn run(&mut self, ops: Vec, asm: &mut Assembler) -> Result<(), Error> { + fn run(ops: Vec, asm: &mut Assembler) -> Result<(), Error> { asm.inspect_macros(ops.clone())?; for rawop in ops { @@ -383,7 +375,7 @@ where } RawOp::Scope(scope_ops) => { let mut new_asm = Assembler::new_internal(asm.concrete_len()); - self.run(scope_ops, &mut new_asm)?; + Self::run(scope_ops, &mut new_asm)?; let raw = new_asm.take(); asm.push(RawOp::Raw(raw))?; } diff --git a/etk-asm/tests/asm.rs b/etk-asm/tests/asm.rs index 4f9c87bb..c09dba9f 100644 --- a/etk-asm/tests/asm.rs +++ b/etk-asm/tests/asm.rs @@ -47,7 +47,7 @@ fn out_of_bounds() { assert_matches!(err, Error::DirectoryTraversal { .. }); } -/*#[test] +#[test] fn subdirectory() -> Result<(), Error> { let mut output = Vec::new(); let mut ingester = Ingest::new(&mut output); @@ -56,7 +56,8 @@ fn subdirectory() -> Result<(), Error> { assert_eq!(output, hex!("63c001c0de60ff")); Ok(()) -}*/ +} + #[test] fn variable_jump() -> Result<(), Error> { let mut output = Vec::new(); @@ -298,22 +299,7 @@ fn test_dynamic_push_and_include() -> Result<(), Error> { let mut ingester = Ingest::new(&mut output); ingester.ingest_file(source(&["variable-push", "main.etk"]))?; - //let str_out: Vec<_> = output.iter().map(|&byte| format!("{:02x}", byte)).collect(); - //println!("output: {:?}", str_out.concat()); - assert_eq!(output, hex!("61025758585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585801010101010158585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858015b58")); Ok(()) } -/* -#[test] -fn test_erc20() -> Result<(), Error> { - let mut output = Vec::new(); - let mut ingester = Ingest::new(&mut output); - ingester.ingest_file(source(&["erc20", "Contract.etk"]))?; - - let str_out: Vec<_> = output.iter().map(|&byte| format!("{:02x}", byte)).collect(); - //assert_eq!(output, hex!("6000600060006000600060006000")); - println!("output: {:?}", str_out.concat()); - Ok(()) -}*/ From e6ff678cc1f5abb21f8daee98102dba9d5ae6215 Mon Sep 17 00:00:00 2001 From: Gaston Zanitti Date: Thu, 5 Oct 2023 11:15:41 -0300 Subject: [PATCH 25/73] Test, code cleaning, etc --- etk-asm/src/asm.rs | 52 +++++++++++++++++++++++++++++++++++-------- etk-asm/src/ingest.rs | 1 - 2 files changed, 43 insertions(+), 10 deletions(-) diff --git a/etk-asm/src/asm.rs b/etk-asm/src/asm.rs index 05b191bf..4ec6d2a5 100644 --- a/etk-asm/src/asm.rs +++ b/etk-asm/src/asm.rs @@ -126,6 +126,17 @@ mod error { #[snafu(backtrace)] source: ParseError, }, + + /// An instruction macro was used without being defined. + #[snafu(display("variable {} inside macro, was never defined", var))] + #[non_exhaustive] + UndeclaredVariableMacro { + /// The variable that was used without being defined. + var: String, + + /// The location of the error. + backtrace: Backtrace, + }, } } @@ -235,7 +246,7 @@ impl Assembler { self.concrete_len } - /// TODO: Add DOC + /// Inspect the macros in a series of [`RawOp`] and declare them in the assembler. pub fn inspect_macros(&mut self, nodes: I) -> Result<(), Error> where I: IntoIterator, @@ -289,7 +300,9 @@ impl Assembler { return error::UndeclaredInstructionMacro { name }.fail(); } - UndefinedVariable { name, .. } => todo!("undefined variable {}", name), + UndefinedVariable { name, .. } => { + return error::UndeclaredVariableMacro { var: name }.fail(); + } }, Err(_) => unreachable!("all ops should be concretizable"), } @@ -362,7 +375,6 @@ impl Assembler { O: Into, { let rop = rop.into(); - println!("pushing {:?}", rop); self.declare_label(&rop)?; @@ -454,8 +466,6 @@ impl Assembler { dynamic_push = true; } }; - //let size = op.size().unwrap_or(2); // %push(label) with min size (to be updated) - //self.concrete_len += size; self.undefined_labels.push(PendingLabel { label: _label.to_owned(), @@ -467,7 +477,9 @@ impl Assembler { UnknownMacro { name, .. } => { return error::UndeclaredInstructionMacro { name }.fail() } - UndefinedVariable { name, .. } => todo!("undefined variable {}", name), + UndefinedVariable { name, .. } => { + return error::UndeclaredVariableMacro { var: name }.fail() + } }, } @@ -1023,8 +1035,6 @@ mod tests { let mut asm = Assembler::new(); asm.inspect_macros(ops.clone())?; let err = asm.push_all(ops).unwrap_err(); - //let err = asm.finish().unwrap_err(); - println!("{:?}", err); assert_matches!(err, Error::UndeclaredInstructionMacro { name, .. } if name == "my_macro"); Ok(()) @@ -1048,7 +1058,6 @@ mod tests { ]; let mut asm = Assembler::new(); let err = asm.inspect_macros(ops.clone()).unwrap_err(); - //let err = asm.push_all(ops).unwrap_err(); assert_matches!(err, Error::DuplicateMacro { name, .. } if name == "my_macro"); Ok(()) @@ -1254,4 +1263,29 @@ mod tests { Ok(()) } + + #[test] + fn assemble_instruction_macro_with_undeclared_variables() { + let ops = vec![ + InstructionMacroDefinition { + name: "my_macro".into(), + parameters: vec!["foo".into()], + contents: vec![AbstractOp::new(Push1(Imm::with_variable("bar")))], + } + .into(), + AbstractOp::Label("b".into()), + AbstractOp::new(JumpDest), + AbstractOp::new(Push1(Imm::with_label("b"))), + AbstractOp::Macro(InstructionMacroInvocation { + name: "my_macro".into(), + parameters: vec![BigInt::from_bytes_be(Sign::Plus, &vec![0x42]).into()], + }), + ]; + + let mut asm = Assembler::new(); + asm.inspect_macros(ops.clone()).unwrap(); + let err = asm.push_all(ops).unwrap_err(); + + assert_matches!(err, Error::UndeclaredVariableMacro { var, .. } if var == "bar"); + } } diff --git a/etk-asm/src/ingest.rs b/etk-asm/src/ingest.rs index 1ce4afc7..b0281894 100644 --- a/etk-asm/src/ingest.rs +++ b/etk-asm/src/ingest.rs @@ -310,7 +310,6 @@ where let nodes = parse_asm(src)?; let mut raws = Vec::new(); for node in nodes { - println!("{:?}", node); match node { Node::Op(op) => { raws.push(RawOp::Op(op)); From 1f4696581f0398421de24bf57c92cbcce1e38347 Mon Sep 17 00:00:00 2001 From: Gaston Zanitti Date: Thu, 5 Oct 2023 11:40:33 -0300 Subject: [PATCH 26/73] Test with short-circuit evaluation --- etk-asm/src/asm.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/etk-asm/src/asm.rs b/etk-asm/src/asm.rs index 4ec6d2a5..d36009c7 100644 --- a/etk-asm/src/asm.rs +++ b/etk-asm/src/asm.rs @@ -1189,7 +1189,7 @@ mod tests { Ok(()) } - /*#[test] + #[test] fn assemble_variable_push_expression_with_undeclared_labels() -> Result<(), Error> { let mut asm = Assembler::new(); asm.push_all(vec![ @@ -1201,9 +1201,10 @@ mod tests { AbstractOp::new(Gas), ])?; let err = asm.finish().unwrap_err(); - assert_matches!(err, Error::UndeclaredLabels { labels, .. } if (labels.contains(&"foo".to_string())) && labels.contains(&"bar".to_string())); + // The expressions have short-circuit evaluation, so only the first label is caught in the error. + assert_matches!(err, Error::UndeclaredLabels { labels, .. } if (labels.contains(&"foo".to_string()))); Ok(()) - }*/ + } #[test] fn assemble_variable_push1_expression() -> Result<(), Error> { From 2c6861158ffb1390ddced2c3c6bc57ada9171827 Mon Sep 17 00:00:00 2001 From: Gaston Zanitti Date: Fri, 6 Oct 2023 10:56:34 -0300 Subject: [PATCH 27/73] Undoing of the modifications to the expected values of the tests. Addressing some of the changes proposed by Sam. --- etk-asm/src/asm.rs | 127 ++++++++++++++++++++---------------------- etk-asm/src/ingest.rs | 6 +- 2 files changed, 62 insertions(+), 71 deletions(-) diff --git a/etk-asm/src/asm.rs b/etk-asm/src/asm.rs index d36009c7..321d0d68 100644 --- a/etk-asm/src/asm.rs +++ b/etk-asm/src/asm.rs @@ -128,7 +128,7 @@ mod error { }, /// An instruction macro was used without being defined. - #[snafu(display("variable {} inside macro, was never defined", var))] + #[snafu(display("variable `{}` inside macro, was never defined", var))] #[non_exhaustive] UndeclaredVariableMacro { /// The variable that was used without being defined. @@ -197,26 +197,26 @@ impl From> for RawOp { /// ``` #[derive(Debug, Default)] pub struct Assembler { - /// Assembled ops, not yet ready to be taken. + /// Assembled ops. ready: Vec, - /// Number of bytes in 'ready' that are ready to be taken. + /// Number of bytes used by the operations in ready. concrete_len: usize, - /// Labels, in `pending`, associated with an `AbstractOp::Label`. + /// Labels associated with an `AbstractOp::Label`. declared_labels: HashMap>, - /// Macros, in `pending`, associated with an `AbstractOp::Macro`. + /// Macros associated with an `AbstractOp::Macro`. declared_macros: HashMap, - /// Labels, in `pending`, that have been referred to (ex. with push) but + /// Labels that have been referred to (ex. with push) but /// have not been declared with an `AbstractOp::Label`. - undefined_labels: Vec, + undeclared_labels: Vec, } /// Struct used to keep track of pending label invocations and their positions in code. #[derive(Debug, Clone)] -pub struct PendingLabel { +struct PendingLabel { /// The name of the label. label: String, @@ -233,19 +233,6 @@ impl Assembler { Self::default() } - /// Create a new `Assembler` with a known length. - pub fn new_internal(actual_len: usize) -> Self { - Self { - concrete_len: actual_len, - ..Self::default() - } - } - - /// Get the number of bytes that can be collected with [`Assembler::take`]. - pub fn concrete_len(&self) -> usize { - self.concrete_len - } - /// Inspect the macros in a series of [`RawOp`] and declare them in the assembler. pub fn inspect_macros(&mut self, nodes: I) -> Result<(), Error> where @@ -284,26 +271,30 @@ impl Assembler { .concretize((&self.declared_labels, &self.declared_macros).into()) { Ok(cop) => cop.assemble(&mut output), - Err(ops::Error::ContextIncomplete { source }) => match source { - UnknownLabel { label: _label, .. } => { - let undefined_names: Vec<_> = self - .undefined_labels - .iter() - .map(|PendingLabel { label, .. }| label.clone()) - .collect(); - return error::UndeclaredLabels { - labels: undefined_names, - } - .fail(); - } - UnknownMacro { name, .. } => { - return error::UndeclaredInstructionMacro { name }.fail(); + Err(ops::Error::ContextIncomplete { + source: UnknownLabel { label: _label, .. }, + }) => { + let undefined_names: Vec<_> = self + .undeclared_labels + .iter() + .map(|PendingLabel { label, .. }| label.clone()) + .collect(); + return error::UndeclaredLabels { + labels: undefined_names, } + .fail(); + } + Err(ops::Error::ContextIncomplete { + source: UnknownMacro { name, .. }, + }) => { + return error::UndeclaredInstructionMacro { name }.fail(); + } + Err(ops::Error::ContextIncomplete { + source: UndefinedVariable { name, .. }, + }) => { + return error::UndeclaredVariableMacro { var: name }.fail(); + } - UndefinedVariable { name, .. } => { - return error::UndeclaredVariableMacro { var: name }.fail(); - } - }, Err(_) => unreachable!("all ops should be concretizable"), } } else if let RawOp::Raw(raw) = op { @@ -317,10 +308,10 @@ impl Assembler { /// Indicate that the input sequence is complete. Returns any errors that /// may remain. pub fn finish(&mut self) -> Result<(), Error> { - if !self.undefined_labels.is_empty() { + if !self.undeclared_labels.is_empty() { return error::UndeclaredLabels { labels: self - .undefined_labels + .undeclared_labels .iter() .map(|l| l.label.to_owned()) .collect::>(), @@ -406,7 +397,7 @@ impl Assembler { match rop { RawOp::Op(AbstractOp::Label(label)) => { let mut dst = 0; - for ul in self.undefined_labels.iter() { + for ul in self.undeclared_labels.iter() { if (ul.label == label) & ul.dynamic_push { // Compensation in case label was dynamically pushed. let mut tmp = @@ -420,7 +411,7 @@ impl Assembler { } } - self.undefined_labels.retain(|l| l.label != *label); + self.undeclared_labels.retain(|l| l.label != *label); self.concrete_len += dst; let old = self @@ -456,31 +447,31 @@ impl Assembler { } .fail() } - Err(ops::Error::ContextIncomplete { source }) => match source { - UnknownLabel { label: _label, .. } => { - let mut dynamic_push = false; - match op.size() { - Some(size) => self.concrete_len += size, - None => { - self.concrete_len += 2; - dynamic_push = true; - } - }; - - self.undefined_labels.push(PendingLabel { - label: _label.to_owned(), - position: self.ready.len(), - dynamic_push, - }); - self.ready.push(rop); - } - UnknownMacro { name, .. } => { - return error::UndeclaredInstructionMacro { name }.fail() - } - UndefinedVariable { name, .. } => { - return error::UndeclaredVariableMacro { var: name }.fail() - } - }, + Err(ops::Error::ContextIncomplete { + source: UnknownLabel { label: _label, .. }, + }) => { + let mut dynamic_push = false; + match op.size() { + Some(size) => self.concrete_len += size, + None => { + self.concrete_len += 2; + dynamic_push = true; + } + }; + + self.undeclared_labels.push(PendingLabel { + label: _label.to_owned(), + position: self.ready.len(), + dynamic_push, + }); + self.ready.push(rop); + } + Err(ops::Error::ContextIncomplete { + source: UnknownMacro { name, .. }, + }) => return error::UndeclaredInstructionMacro { name }.fail(), + Err(ops::Error::ContextIncomplete { + source: UndefinedVariable { name, .. }, + }) => return error::UndeclaredVariableMacro { var: name }.fail(), } Ok(()) diff --git a/etk-asm/src/ingest.rs b/etk-asm/src/ingest.rs index b0281894..72b8284b 100644 --- a/etk-asm/src/ingest.rs +++ b/etk-asm/src/ingest.rs @@ -373,7 +373,7 @@ where asm.push(RawOp::Op(op))?; } RawOp::Scope(scope_ops) => { - let mut new_asm = Assembler::new_internal(asm.concrete_len()); + let mut new_asm = Assembler::new(); Self::run(scope_ops, &mut new_asm)?; let raw = new_asm.take(); asm.push(RawOp::Raw(raw))?; @@ -459,7 +459,7 @@ mod tests { let mut ingest = Ingest::new(&mut output); ingest.ingest(root, &text)?; - assert_eq!(output, hex!("60015b586002566002")); + assert_eq!(output, hex!("60015b586000566002")); Ok(()) } @@ -651,7 +651,7 @@ mod tests { let mut ingest = Ingest::new(&mut output); ingest.ingest(root, &text)?; - let expected = hex!("620000155b5860105b5b6100106100085b600860095b62000004"); + let expected = hex!("620000155b58600b5b5b61000b6100035b600360045b62000004"); assert_eq!(output, expected); Ok(()) From ee041572eed2965509cbb121ba06f1a76f71f76e Mon Sep 17 00:00:00 2001 From: Gaston Zanitti Date: Fri, 6 Oct 2023 13:38:48 -0300 Subject: [PATCH 28/73] Code restructuring. push_all replaced by assemble --- etk-asm/src/asm.rs | 274 ++++++++++++++++++------------------------ etk-asm/src/ingest.rs | 29 +---- 2 files changed, 119 insertions(+), 184 deletions(-) diff --git a/etk-asm/src/asm.rs b/etk-asm/src/asm.rs index 321d0d68..275bb9d1 100644 --- a/etk-asm/src/asm.rs +++ b/etk-asm/src/asm.rs @@ -187,12 +187,10 @@ impl From> for RawOp { /// # /// # use hex_literal::hex; /// let mut asm = Assembler::new(); -/// asm.push_all(vec![ +/// let result = asm.assemble(vec![ /// AbstractOp::new(GetPc), /// ])?; -/// let output = asm.take(); -/// asm.finish()?; -/// # assert_eq!(output, hex!("58")); +/// # assert_eq!(result, hex!("58")); /// # Result::<(), Error>::Ok(()) /// ``` #[derive(Debug, Default)] @@ -234,12 +232,12 @@ impl Assembler { } /// Inspect the macros in a series of [`RawOp`] and declare them in the assembler. - pub fn inspect_macros(&mut self, nodes: I) -> Result<(), Error> + fn inspect_macros(&mut self, nodes: &I) -> Result<(), Error> where - I: IntoIterator, + I: IntoIterator + Clone, O: Into, { - for op in nodes { + for op in nodes.clone() { let op = op.into(); if let RawOp::Op(AbstractOp::MacroDefinition(_)) = op { self.declare_macro(op)? @@ -250,7 +248,7 @@ impl Assembler { } /// Collect any assembled instructions that are ready to be output. - pub fn take(&mut self) -> Vec { + fn take(&mut self) -> Vec { let output = self.concretize_ops(); match output { Ok(v) => { @@ -274,13 +272,13 @@ impl Assembler { Err(ops::Error::ContextIncomplete { source: UnknownLabel { label: _label, .. }, }) => { - let undefined_names: Vec<_> = self + let undeclared_names: Vec<_> = self .undeclared_labels .iter() .map(|PendingLabel { label, .. }| label.clone()) .collect(); return error::UndeclaredLabels { - labels: undefined_names, + labels: undeclared_names, } .fail(); } @@ -305,9 +303,9 @@ impl Assembler { Ok(output) } - /// Indicate that the input sequence is complete. Returns any errors that + /// Check if the input sequence is complete. Returns any errors that /// may remain. - pub fn finish(&mut self) -> Result<(), Error> { + fn finish(&mut self) -> Result<(), Error> { if !self.undeclared_labels.is_empty() { return error::UndeclaredLabels { labels: self @@ -324,21 +322,24 @@ impl Assembler { /// Feed instructions into the `Assembler`. /// - /// Returns the number of bytes that can be collected with [`Assembler::take`]. - pub fn push_all(&mut self, ops: I) -> Result + /// Returns the code of the assembled program. + pub fn assemble(&mut self, ops: I) -> Result, Error> where - I: IntoIterator, + I: IntoIterator + Clone, O: Into, { + self.inspect_macros(&ops)?; + for op in ops { self.push(op)?; } - Ok(self.concrete_len) + self.finish()?; + + Ok(self.take()) } - /// Insert explicilty declared macros and labels, via `AbstractOp`, and implictly declared - /// macros and labels via usage in `Op`. + /// Insert explicitly declared macros, via `AbstractOp`, into the `Assembler`. fn declare_macro(&mut self, rop: O) -> Result<(), Error> where O: Into, @@ -361,7 +362,7 @@ impl Assembler { /// Feed a single instruction into the `Assembler`. /// /// Returns the number of bytes that can be collected with [`Assembler::take`] - pub fn push(&mut self, rop: O) -> Result + fn push(&mut self, rop: O) -> Result where O: Into, { @@ -481,7 +482,13 @@ impl Assembler { self.ready.push(RawOp::Raw(raw)); Ok(()) } - RawOp::Scope(_) => unreachable!("scopes should be expanded before being pushed"), + RawOp::Scope(scope) => { + let mut asm = Self::new(); + let scope_result = asm.assemble(scope)?; + self.concrete_len += scope_result.len(); + self.ready.push(RawOp::Raw(scope_result)); + Ok(()) + } } } @@ -542,7 +549,10 @@ impl Assembler { } } - Ok(Some(self.push_all(m.contents)?)) + for op in m.contents.iter() { + self.push(op.clone())?; + } + Ok(Some(self.concrete_len)) } _ => error::UndeclaredInstructionMacro { name }.fail(), } @@ -564,20 +574,19 @@ mod tests { #[test] fn assemble_variable_push_const_while_pending() -> Result<(), Error> { let mut asm = Assembler::new(); - let sz = asm.push_all(vec![ + let result = asm.assemble(vec![ AbstractOp::Op(Push1(Imm::with_label("label1")).into()), AbstractOp::Push(Terminal::Number(0xaabb.into()).into()), AbstractOp::Label("label1".into()), ])?; - assert_eq!(5, sz); - assert_eq!(asm.take(), hex!("600561aabb")); + assert_eq!(result, hex!("600561aabb")); Ok(()) } #[test] fn assemble_variable_pushes_abab() -> Result<(), Error> { let mut asm = Assembler::new(); - let sz = asm.push_all(vec![ + let result = asm.assemble(vec![ AbstractOp::new(JumpDest), AbstractOp::Push(Imm::with_label("label1")), AbstractOp::Push(Imm::with_label("label2")), @@ -586,15 +595,14 @@ mod tests { AbstractOp::Label("label2".into()), AbstractOp::new(GetPc), ])?; - assert_eq!(7, sz); - assert_eq!(asm.take(), hex!("5b600560065858")); + assert_eq!(result, hex!("5b600560065858")); Ok(()) } #[test] fn assemble_variable_pushes_abba() -> Result<(), Error> { let mut asm = Assembler::new(); - let sz = asm.push_all(vec![ + let result = asm.assemble(vec![ AbstractOp::new(JumpDest), AbstractOp::Push(Imm::with_label("label1")), AbstractOp::Push(Imm::with_label("label2")), @@ -603,33 +611,30 @@ mod tests { AbstractOp::Label("label1".into()), AbstractOp::new(GetPc), ])?; - assert_eq!(7, sz); - assert_eq!(asm.take(), hex!("5b600660055858")); + assert_eq!(result, hex!("5b600660055858")); Ok(()) } #[test] fn assemble_variable_push1_multiple() -> Result<(), Error> { let mut asm = Assembler::new(); - let sz = asm.push_all(vec![ + let result = asm.assemble(vec![ AbstractOp::new(JumpDest), AbstractOp::Push(Imm::with_label("auto")), AbstractOp::Push(Imm::with_label("auto")), AbstractOp::Label("auto".into()), ])?; - assert_eq!(5, sz); - assert_eq!(asm.take(), hex!("5b60056005")); + assert_eq!(result, hex!("5b60056005")); Ok(()) } #[test] fn assemble_variable_push_const() -> Result<(), Error> { let mut asm = Assembler::new(); - let sz = asm.push_all(vec![AbstractOp::Push( + let result = asm.assemble(vec![AbstractOp::Push( Terminal::Number((0x00aaaaaaaaaaaaaaaaaaaaaaaa as u128).into()).into(), )])?; - assert_eq!(13, sz); - assert_eq!(asm.take(), hex!("6baaaaaaaaaaaaaaaaaaaaaaaa")); + assert_eq!(result, hex!("6baaaaaaaaaaaaaaaaaaaaaaaa")); Ok(()) } @@ -639,7 +644,7 @@ mod tests { let mut asm = Assembler::new(); let err = asm - .push_all(vec![AbstractOp::Push(Terminal::Number(v).into())]) + .assemble(vec![AbstractOp::Push(Terminal::Number(v).into())]) .unwrap_err(); assert_matches!(err, Error::ExpressionTooLarge { .. }); @@ -649,7 +654,7 @@ mod tests { fn assemble_variable_push_negative() { let mut asm = Assembler::new(); let err = asm - .push_all(vec![AbstractOp::Push(Terminal::Number((-1).into()).into())]) + .assemble(vec![AbstractOp::Push(Terminal::Number((-1).into()).into())]) .unwrap_err(); assert_matches!(err, Error::ExpressionNegative { .. }); @@ -658,73 +663,68 @@ mod tests { #[test] fn assemble_variable_push_const0() -> Result<(), Error> { let mut asm = Assembler::new(); - let sz = asm.push_all(vec![AbstractOp::Push( + let result = asm.assemble(vec![AbstractOp::Push( Terminal::Number((0x00 as u128).into()).into(), )])?; - assert_eq!(2, sz); - assert_eq!(asm.take(), hex!("6000")); + assert_eq!(result, hex!("6000")); Ok(()) } #[test] fn assemble_variable_push1_known() -> Result<(), Error> { let mut asm = Assembler::new(); - let sz = asm.push_all(vec![ + let result = asm.assemble(vec![ AbstractOp::new(JumpDest), AbstractOp::Label("auto".into()), AbstractOp::Push(Imm::with_label("auto")), ])?; - assert_eq!(3, sz); - assert_eq!(asm.take(), hex!("5b6001")); + assert_eq!(result, hex!("5b6001")); Ok(()) } #[test] fn assemble_variable_push1() -> Result<(), Error> { let mut asm = Assembler::new(); - let sz = asm.push_all(vec![ + let result = asm.assemble(vec![ AbstractOp::Push(Imm::with_label("auto")), AbstractOp::Label("auto".into()), AbstractOp::new(JumpDest), ])?; - assert_eq!(3, sz); - assert_eq!(asm.take(), hex!("60025b")); + assert_eq!(result, hex!("60025b")); Ok(()) } #[test] fn assemble_variable_push1_reuse() -> Result<(), Error> { let mut asm = Assembler::new(); - let sz = asm.push_all(vec![ + let result = asm.assemble(vec![ AbstractOp::Push(Imm::with_label("auto")), AbstractOp::Label("auto".into()), AbstractOp::new(JumpDest), AbstractOp::new(Push1(Imm::with_label("auto"))), ])?; - assert_eq!(5, sz); - assert_eq!(asm.take(), hex!("60025b6002")); + assert_eq!(result, hex!("60025b6002")); Ok(()) } #[test] fn assemble_variable_push2() -> Result<(), Error> { - let mut asm = Assembler::new(); - asm.push(AbstractOp::Push(Imm::with_label("auto")))?; + let mut code = vec![]; + code.push(AbstractOp::Push(Imm::with_label("auto"))); for _ in 0..255 { - asm.push(AbstractOp::new(GetPc))?; + code.push(AbstractOp::new(GetPc)); } - asm.push_all(vec![ - AbstractOp::Label("auto".into()), - AbstractOp::new(JumpDest), - ])?; + code.push(AbstractOp::Label("auto".into())); + code.push(AbstractOp::new(JumpDest)); + + let mut asm = Assembler::new(); + let result = asm.assemble(code)?; let mut expected = vec![0x61, 0x01, 0x02]; expected.extend_from_slice(&[0x58; 255]); expected.push(0x5b); - assert_eq!(asm.take(), expected); - - asm.finish()?; + assert_eq!(result, expected); Ok(()) } @@ -732,8 +732,9 @@ mod tests { #[test] fn assemble_undeclared_label() -> Result<(), Error> { let mut asm = Assembler::new(); - asm.push_all(vec![AbstractOp::new(Push1(Imm::with_label("hi")))])?; - let err = asm.finish().unwrap_err(); + let err = asm + .assemble(vec![AbstractOp::new(Push1(Imm::with_label("hi")))]) + .unwrap_err(); assert_matches!(err, Error::UndeclaredLabels { labels, .. } if labels == vec!["hi"]); Ok(()) } @@ -741,10 +742,9 @@ mod tests { #[test] fn assemble_jumpdest_no_label() -> Result<(), Error> { let mut asm = Assembler::new(); - let sz = asm.push_all(vec![AbstractOp::new(JumpDest)])?; - assert_eq!(1, sz); + let result = asm.assemble(vec![AbstractOp::new(JumpDest)])?; assert!(asm.declared_labels.is_empty()); - assert_eq!(asm.take(), hex!("5b")); + assert_eq!(result, hex!("5b")); Ok(()) } @@ -753,11 +753,10 @@ mod tests { let mut asm = Assembler::new(); let ops = vec![AbstractOp::Label("lbl".into()), AbstractOp::new(JumpDest)]; - let sz = asm.push_all(ops)?; - assert_eq!(1, sz); + let result = asm.assemble(ops)?; assert_eq!(asm.declared_labels.len(), 1); assert_eq!(asm.declared_labels.get("lbl"), Some(&Some(0))); - assert_eq!(asm.take(), hex!("5b")); + assert_eq!(result, hex!("5b")); Ok(()) } @@ -770,9 +769,8 @@ mod tests { ]; let mut asm = Assembler::new(); - let sz = asm.push_all(ops)?; - assert_eq!(sz, 3); - assert_eq!(asm.take(), hex!("5b6000")); + let result = asm.assemble(ops)?; + assert_eq!(result, hex!("5b6000")); Ok(()) } @@ -786,9 +784,8 @@ mod tests { ]; let mut asm = Assembler::new(); - let sz = asm.push_all(ops)?; - assert_eq!(sz, 3); - assert_eq!(asm.take(), hex!("600258")); + let result = asm.assemble(ops)?; + assert_eq!(result, hex!("600258")); Ok(()) } @@ -802,9 +799,8 @@ mod tests { ]; let mut asm = Assembler::new(); - let sz = asm.push_all(ops)?; - assert_eq!(sz, 3); - assert_eq!(asm.take(), hex!("60025b")); + let result = asm.assemble(ops)?; + assert_eq!(result, hex!("60025b")); Ok(()) } @@ -818,7 +814,7 @@ mod tests { ops.push(AbstractOp::new(JumpDest)); ops.push(AbstractOp::new(Push1(Imm::with_label("a")))); let mut asm = Assembler::new(); - let err = asm.push_all(ops).unwrap_err(); + let err = asm.assemble(ops).unwrap_err(); assert_matches!(err, Error::ExpressionTooLarge { expr: Expression::Terminal(Terminal::Label(label)), .. } if label == "a"); } @@ -831,11 +827,7 @@ mod tests { ops.push(AbstractOp::new(JumpDest)); ops.push(AbstractOp::new(Push1(Imm::with_label("b")))); let mut asm = Assembler::new(); - let sz = asm.push_all(ops)?; - assert_eq!(sz, 259); - - let assembled = asm.take(); - asm.finish()?; + let result = asm.assemble(ops)?; let mut expected = vec![0x58; 255]; expected.push(0x5b); @@ -843,7 +835,7 @@ mod tests { expected.push(0x60); expected.push(0xff); - assert_eq!(assembled, expected); + assert_eq!(result, expected); Ok(()) } @@ -874,11 +866,8 @@ mod tests { ]; let mut asm = Assembler::new(); - asm.inspect_macros(ops.clone())?; - let sz = asm.push_all(ops)?; - assert_eq!(sz, 0); - let out = asm.take(); - assert_eq!(out, []); + let result = asm.assemble(ops)?; + assert_eq!(result, []); Ok(()) } @@ -911,11 +900,8 @@ mod tests { ]; let mut asm = Assembler::new(); - asm.inspect_macros(ops.clone())?; - let sz = asm.push_all(ops)?; - assert_eq!(sz, 13); - let out = asm.take(); - assert_eq!(out, hex!("5b60005b600360005b60086000")); + let result = asm.assemble(ops)?; + assert_eq!(result, hex!("5b60005b600360005b60086000")); Ok(()) } @@ -944,11 +930,8 @@ mod tests { ]; let mut asm = Assembler::new(); - asm.inspect_macros(ops.clone())?; - let sz = asm.push_all(ops)?; - assert_eq!(sz, 8); - let out = asm.take(); - assert_eq!(out, hex!("5b60005b60036000")); + let result = asm.assemble(ops)?; + assert_eq!(result, hex!("5b60005b60036000")); Ok(()) } @@ -977,11 +960,8 @@ mod tests { ]; let mut asm = Assembler::new(); - asm.inspect_macros(ops.clone())?; - let sz = asm.push_all(ops)?; - assert_eq!(sz, 8); - let out = asm.take(); - assert_eq!(out, hex!("5b60005b60036000")); + let result = asm.assemble(ops)?; + assert_eq!(result, hex!("5b60005b60036000")); Ok(()) } @@ -1010,10 +990,8 @@ mod tests { ]; let mut asm = Assembler::new(); - asm.inspect_macros(ops.clone())?; - let sz = asm.push_all(ops)?; - assert_eq!(7, sz); - assert_eq!(asm.take(), hex!("5b600560065858")); + let result = asm.assemble(ops)?; + assert_eq!(result, hex!("5b600560065858")); Ok(()) } @@ -1024,8 +1002,7 @@ mod tests { InstructionMacroInvocation::with_zero_parameters("my_macro".into()), )]; let mut asm = Assembler::new(); - asm.inspect_macros(ops.clone())?; - let err = asm.push_all(ops).unwrap_err(); + let err = asm.assemble(ops).unwrap_err(); assert_matches!(err, Error::UndeclaredInstructionMacro { name, .. } if name == "my_macro"); Ok(()) @@ -1048,7 +1025,7 @@ mod tests { .into(), ]; let mut asm = Assembler::new(); - let err = asm.inspect_macros(ops.clone()).unwrap_err(); + let err = asm.assemble(ops).unwrap_err(); assert_matches!(err, Error::DuplicateMacro { name, .. } if name == "my_macro"); Ok(()) @@ -1068,8 +1045,7 @@ mod tests { )), ]; let mut asm = Assembler::new(); - asm.inspect_macros(ops.clone())?; - let err = asm.push_all(ops).unwrap_err(); + let err = asm.assemble(ops).unwrap_err(); assert_matches!(err, Error::DuplicateLabel { label, .. } if label == "a"); Ok(()) @@ -1096,14 +1072,9 @@ mod tests { AbstractOp::new(Push1(Imm::with_label("a"))), ]; let mut asm = Assembler::new(); - asm.inspect_macros(ops.clone())?; - let sz = asm.push_all(ops)?; - assert_eq!(sz, 5); - - let out = asm.take(); - asm.finish()?; + let result = asm.assemble(ops)?; - assert_eq!(out, hex!("3360016000")); + assert_eq!(result, hex!("3360016000")); Ok(()) } @@ -1133,11 +1104,8 @@ mod tests { ]; let mut asm = Assembler::new(); - asm.inspect_macros(ops.clone())?; - let sz = asm.push_all(ops)?; - assert_eq!(sz, 7); - let out = asm.take(); - assert_eq!(out, hex!("5b600060426000")); + let result = asm.assemble(ops)?; + assert_eq!(result, hex!("5b600060426000")); Ok(()) } @@ -1149,10 +1117,8 @@ mod tests { )))]; let mut asm = Assembler::new(); - let sz = asm.push_all(ops)?; - assert_eq!(sz, 2); - let out = asm.take(); - assert_eq!(out, hex!("6002")); + let result = asm.assemble(ops)?; + assert_eq!(result, hex!("6002")); Ok(()) } @@ -1163,7 +1129,7 @@ mod tests { BigInt::from(-1).into(), )))]; let mut asm = Assembler::new(); - let err = asm.push_all(ops).unwrap_err(); + let err = asm.assemble(ops).unwrap_err(); assert_matches!(err, Error::ExpressionNegative { value, .. } if value == BigInt::from(-1)); Ok(()) @@ -1172,10 +1138,11 @@ mod tests { #[test] fn assemble_expression_undeclared_label() -> Result<(), Error> { let mut asm = Assembler::new(); - asm.push_all(vec![AbstractOp::new(Push1(Imm::with_expression( - Terminal::Label(String::from("hi")).into(), - )))])?; - let err = asm.finish().unwrap_err(); + let err = asm + .assemble(vec![AbstractOp::new(Push1(Imm::with_expression( + Terminal::Label(String::from("hi")).into(), + )))]) + .unwrap_err(); assert_matches!(err, Error::UndeclaredLabels { labels, .. } if labels == vec!["hi"]); Ok(()) } @@ -1183,15 +1150,16 @@ mod tests { #[test] fn assemble_variable_push_expression_with_undeclared_labels() -> Result<(), Error> { let mut asm = Assembler::new(); - asm.push_all(vec![ - AbstractOp::new(JumpDest), - AbstractOp::Push(Imm::with_expression(Expression::Plus( - Terminal::Label("foo".into()).into(), - Terminal::Label("bar".into()).into(), - ))), - AbstractOp::new(Gas), - ])?; - let err = asm.finish().unwrap_err(); + let err = asm + .assemble(vec![ + AbstractOp::new(JumpDest), + AbstractOp::Push(Imm::with_expression(Expression::Plus( + Terminal::Label("foo".into()).into(), + Terminal::Label("bar".into()).into(), + ))), + AbstractOp::new(Gas), + ]) + .unwrap_err(); // The expressions have short-circuit evaluation, so only the first label is caught in the error. assert_matches!(err, Error::UndeclaredLabels { labels, .. } if (labels.contains(&"foo".to_string()))); Ok(()) @@ -1200,7 +1168,7 @@ mod tests { #[test] fn assemble_variable_push1_expression() -> Result<(), Error> { let mut asm = Assembler::new(); - let sz = asm.push_all(vec![ + let result = asm.assemble(vec![ AbstractOp::new(JumpDest), AbstractOp::Label("auto".into()), AbstractOp::Push(Imm::with_expression(Expression::Plus( @@ -1208,15 +1176,14 @@ mod tests { Terminal::Label(String::from("auto")).into(), ))), ])?; - assert_eq!(3, sz); - assert_eq!(asm.take(), hex!("5b6002")); + assert_eq!(result, hex!("5b6002")); Ok(()) } #[test] fn assemble_expression_with_labels() -> Result<(), Error> { let mut asm = Assembler::new(); - let sz = asm.push_all(vec![ + let result = asm.assemble(vec![ AbstractOp::new(JumpDest), AbstractOp::Push(Imm::with_expression(Expression::Plus( Terminal::Label(String::from("foo")).into(), @@ -1226,8 +1193,7 @@ mod tests { AbstractOp::Label("foo".into()), AbstractOp::Label("bar".into()), ])?; - assert_eq!(4, sz); - assert_eq!(asm.take(), hex!("5b60085a")); + assert_eq!(result, hex!("5b60085a")); Ok(()) } @@ -1247,11 +1213,8 @@ mod tests { ]; let mut asm = Assembler::new(); - asm.inspect_macros(ops.clone())?; - let sz = asm.push_all(ops)?; - assert_eq!(sz, 2); - let out = asm.take(); - assert_eq!(out, hex!("6002")); + let result = asm.assemble(ops)?; + assert_eq!(result, hex!("6002")); Ok(()) } @@ -1275,8 +1238,7 @@ mod tests { ]; let mut asm = Assembler::new(); - asm.inspect_macros(ops.clone()).unwrap(); - let err = asm.push_all(ops).unwrap_err(); + let err = asm.assemble(ops).unwrap_err(); assert_matches!(err, Error::UndeclaredVariableMacro { var, .. } if var == "bar"); } diff --git a/etk-asm/src/ingest.rs b/etk-asm/src/ingest.rs index 72b8284b..bc5e4bfd 100644 --- a/etk-asm/src/ingest.rs +++ b/etk-asm/src/ingest.rs @@ -294,9 +294,7 @@ where let mut program = Program::new(path.into()); let nodes = self.preprocess(&mut program, text)?; let mut asm = Assembler::new(); - Self::run(nodes, &mut asm)?; - - let raw = asm.take(); + let raw = asm.assemble(nodes)?; self.output.write_all(&raw).context(error::Io { message: "writing output", @@ -363,31 +361,6 @@ where program.pop_path(); Ok(new_raws) } - - fn run(ops: Vec, asm: &mut Assembler) -> Result<(), Error> { - asm.inspect_macros(ops.clone())?; - - for rawop in ops { - match rawop { - RawOp::Op(op) => { - asm.push(RawOp::Op(op))?; - } - RawOp::Scope(scope_ops) => { - let mut new_asm = Assembler::new(); - Self::run(scope_ops, &mut new_asm)?; - let raw = new_asm.take(); - asm.push(RawOp::Raw(raw))?; - } - RawOp::Raw(hex) => { - asm.push(RawOp::Raw(hex))?; - } - } - } - - asm.finish()?; - - Ok(()) - } } #[cfg(test)] From 859e85b35eec8d028ce4128905a50c3176d3d328 Mon Sep 17 00:00:00 2001 From: Gaston Zanitti Date: Fri, 6 Oct 2023 14:04:18 -0300 Subject: [PATCH 29/73] Minor changes in docs --- etk-asm/src/asm.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/etk-asm/src/asm.rs b/etk-asm/src/asm.rs index 275bb9d1..5ff555f3 100644 --- a/etk-asm/src/asm.rs +++ b/etk-asm/src/asm.rs @@ -154,7 +154,7 @@ pub enum RawOp { /// An instruction to be assembled. Op(AbstractOp), - /// A new scope to be included. + /// A new scope to be created with its corresponding list of operations. Scope(Vec), /// Raw bytes, for example from `%include_hex`, to be included verbatim in @@ -198,7 +198,7 @@ pub struct Assembler { /// Assembled ops. ready: Vec, - /// Number of bytes used by the operations in ready. + /// Number of bytes used by the operations in `ready``. concrete_len: usize, /// Labels associated with an `AbstractOp::Label`. @@ -360,8 +360,6 @@ impl Assembler { } /// Feed a single instruction into the `Assembler`. - /// - /// Returns the number of bytes that can be collected with [`Assembler::take`] fn push(&mut self, rop: O) -> Result where O: Into, From 7bbfe8e9ab77fa278f54645841aec0efdb8c3914 Mon Sep 17 00:00:00 2001 From: Gaston Zanitti Date: Fri, 6 Oct 2023 16:13:42 -0300 Subject: [PATCH 30/73] Added tests mentioned in issue #108 --- etk-asm/src/asm.rs | 66 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/etk-asm/src/asm.rs b/etk-asm/src/asm.rs index 5ff555f3..3e5556db 100644 --- a/etk-asm/src/asm.rs +++ b/etk-asm/src/asm.rs @@ -1240,4 +1240,70 @@ mod tests { assert_matches!(err, Error::UndeclaredVariableMacro { var, .. } if var == "bar"); } + + #[test] + fn assemble_instruction_macro_two_delayed_definitions_mirrored() -> Result<(), Error> { + let ops = vec![ + AbstractOp::new(GetPc), + AbstractOp::Macro(InstructionMacroInvocation { + name: "macro1".into(), + parameters: vec![], + }), + AbstractOp::Macro(InstructionMacroInvocation { + name: "macro0".into(), + parameters: vec![], + }), + InstructionMacroDefinition { + name: "macro0".into(), + parameters: vec![], + contents: vec![AbstractOp::new(JumpDest)], + } + .into(), + InstructionMacroDefinition { + name: "macro1".into(), + parameters: vec![], + contents: vec![AbstractOp::new(Caller)], + } + .into(), + ]; + + let mut asm = Assembler::new(); + let result = asm.assemble(ops)?; + assert_eq!(result, hex!("58335b")); + + Ok(()) + } + + #[test] + fn assemble_instruction_macro_two_delayed_definitions() -> Result<(), Error> { + let ops = vec![ + AbstractOp::new(GetPc), + AbstractOp::Macro(InstructionMacroInvocation { + name: "macro0".into(), + parameters: vec![], + }), + AbstractOp::Macro(InstructionMacroInvocation { + name: "macro1".into(), + parameters: vec![], + }), + InstructionMacroDefinition { + name: "macro0".into(), + parameters: vec![], + contents: vec![AbstractOp::new(JumpDest)], + } + .into(), + InstructionMacroDefinition { + name: "macro1".into(), + parameters: vec![], + contents: vec![AbstractOp::new(Caller)], + } + .into(), + ]; + + let mut asm = Assembler::new(); + let result = asm.assemble(ops)?; + assert_eq!(result, hex!("585b33")); + + Ok(()) + } } From 6a25d9ddd324747635053da64383ff4943ba1c79 Mon Sep 17 00:00:00 2001 From: Gaston Zanitti Date: Tue, 10 Oct 2023 13:42:38 -0300 Subject: [PATCH 31/73] Better error during parsing. Issue #82 --- etk-asm/src/ingest.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/etk-asm/src/ingest.rs b/etk-asm/src/ingest.rs index bc5e4bfd..5d969f79 100644 --- a/etk-asm/src/ingest.rs +++ b/etk-asm/src/ingest.rs @@ -51,13 +51,15 @@ mod error { }, /// An error that occurred while parsing a file. - #[snafu(context(false))] #[non_exhaustive] - #[snafu(display("parsing failed"))] + #[snafu(display("parsing failed on path `{}`", path.to_string_lossy()))] Parse { /// The underlying source of this error. #[snafu(backtrace)] source: ParseError, + + /// The location of the error. + path: PathBuf, }, /// An error that occurred while assembling a file. @@ -305,7 +307,9 @@ where } fn preprocess(&mut self, program: &mut Program, src: &str) -> Result, Error> { - let nodes = parse_asm(src)?; + let nodes = parse_asm(src).context(error::Parse { + path: program.sources.last().unwrap().clone(), + })?; let mut raws = Vec::new(); for node in nodes { match node { From 5ad5da008ac3723e6f8d4506bd942be2f96f832d Mon Sep 17 00:00:00 2001 From: Gaston Zanitti Date: Thu, 12 Oct 2023 16:42:52 -0300 Subject: [PATCH 32/73] Hardfork selection minimal backbone --- etk-asm/src/bin/eas.rs | 14 +++++++++++++- etk-asm/src/ingest.rs | 28 +++++++++++++++------------- etk-ops/src/lib.rs | 40 +++++++++++++++++++++++++++++++++++++++- 3 files changed, 67 insertions(+), 15 deletions(-) diff --git a/etk-asm/src/bin/eas.rs b/etk-asm/src/bin/eas.rs index cf3285cd..a5ab83f8 100644 --- a/etk-asm/src/bin/eas.rs +++ b/etk-asm/src/bin/eas.rs @@ -2,6 +2,7 @@ use etk_cli::errors::WithSources; use etk_cli::io::HexWrite; use etk_asm::ingest::{Error, Ingest}; +use etk_ops::HardFork; use std::fs::File; use std::io::prelude::*; @@ -16,6 +17,9 @@ struct Opt { input: PathBuf, #[structopt(parse(from_os_str))] out: Option, + + #[structopt(long, parse(from_os_str))] + hardfork: Option, } fn create(path: PathBuf) -> File { @@ -43,9 +47,17 @@ fn run() -> Result<(), Error> { None => Box::new(std::io::stdout()), }; + let opthard = opt.hardfork.clone(); + let mut hardfork = match opt.hardfork { + Some(h) => h, + None => HardFork::default(), + }; + + println!("Hardfork: {:?}.", hardfork); + let hex_out = HexWrite::new(&mut out); - let mut ingest = Ingest::new(hex_out); + let mut ingest = Ingest::new(hex_out, hardfork); ingest.ingest_file(opt.input)?; out.write_all(b"\n").unwrap(); diff --git a/etk-asm/src/ingest.rs b/etk-asm/src/ingest.rs index 5d969f79..c90d5077 100644 --- a/etk-asm/src/ingest.rs +++ b/etk-asm/src/ingest.rs @@ -102,6 +102,7 @@ use crate::parse::parse_asm; pub use self::error::Error; +use etk_ops::HardFork; use snafu::{ensure, ResultExt}; use std::fs::{read_to_string, File}; @@ -243,7 +244,7 @@ impl Program { /// "#; /// /// let mut output = Vec::new(); -/// let mut ingest = Ingest::new(&mut output); +/// let mut ingest = Ingest::new(&mut output, HardFork::default()); /// ingest.ingest("./example.etk", &text)?; /// /// # let expected = hex!("6100035b"); @@ -253,12 +254,13 @@ impl Program { #[derive(Debug)] pub struct Ingest { output: W, + hardfork: HardFork, } impl Ingest { /// Make a new `Ingest` that writes assembled bytes to `output`. - pub fn new(output: W) -> Self { - Self { output } + pub fn new(output: W, hardfork: HardFork) -> Self { + Self { output, hardfork } } } @@ -404,7 +406,7 @@ mod tests { ); let mut output = Vec::new(); - let mut ingest = Ingest::new(&mut output); + let mut ingest = Ingest::new(&mut output, HardFork::default()); ingest.ingest(root, &text)?; assert_eq!(output, hex!("6001602a6002")); @@ -433,7 +435,7 @@ mod tests { ); let mut output = Vec::new(); - let mut ingest = Ingest::new(&mut output); + let mut ingest = Ingest::new(&mut output, HardFork::default()); ingest.ingest(root, &text)?; assert_eq!(output, hex!("60015b586000566002")); @@ -462,7 +464,7 @@ mod tests { ); let mut output = Vec::new(); - let mut ingest = Ingest::new(&mut output); + let mut ingest = Ingest::new(&mut output, HardFork::default()); let err = ingest.ingest(root, &text).unwrap_err(); assert_matches!( @@ -487,7 +489,7 @@ mod tests { ); let mut output = Vec::new(); - let mut ingest = Ingest::new(&mut output); + let mut ingest = Ingest::new(&mut output, HardFork::default()); ingest.ingest(root, &text)?; assert_eq!(output, hex!("6001deadbeef0102f66002")); @@ -511,7 +513,7 @@ mod tests { ); let mut output = Vec::new(); - let mut ingest = Ingest::new(&mut output); + let mut ingest = Ingest::new(&mut output, HardFork::default()); ingest.ingest(root, &text)?; assert_eq!(output, hex!("6001deadbeef0102f65b600960ff")); @@ -533,7 +535,7 @@ mod tests { ); let mut output = Vec::new(); - let mut ingest = Ingest::new(&mut output); + let mut ingest = Ingest::new(&mut output, HardFork::default()); ingest.ingest(root, &text)?; let expected = hex!("61001caaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa5b"); @@ -576,7 +578,7 @@ mod tests { ); let mut output = Vec::new(); - let mut ingest = Ingest::new(&mut output); + let mut ingest = Ingest::new(&mut output, HardFork::default()); ingest.ingest(root, &text)?; let expected = hex!("620000096200000e5b5b6008600e5b610008610009"); @@ -625,7 +627,7 @@ mod tests { ); let mut output = Vec::new(); - let mut ingest = Ingest::new(&mut output); + let mut ingest = Ingest::new(&mut output, HardFork::default()); ingest.ingest(root, &text)?; let expected = hex!("620000155b58600b5b5b61000b6100035b600360045b62000004"); @@ -646,7 +648,7 @@ mod tests { ); let mut output = Vec::new(); - let mut ingest = Ingest::new(&mut output); + let mut ingest = Ingest::new(&mut output, HardFork::default()); let root = std::env::current_exe().unwrap(); let err = ingest.ingest(root, &text).unwrap_err(); @@ -667,7 +669,7 @@ mod tests { ); let mut output = Vec::new(); - let mut ingest = Ingest::new(&mut output); + let mut ingest = Ingest::new(&mut output, HardFork::default()); let err = ingest.ingest(root, &text).unwrap_err(); assert_matches!(err, Error::RecursionLimit { .. }); diff --git a/etk-ops/src/lib.rs b/etk-ops/src/lib.rs index 52520016..33e2bf15 100644 --- a/etk-ops/src/lib.rs +++ b/etk-ops/src/lib.rs @@ -12,7 +12,10 @@ use snafu::{Backtrace, Snafu}; -use std::borrow::{Borrow, BorrowMut}; +use std::{ + borrow::{Borrow, BorrowMut}, + ffi::OsStr, +}; pub mod london { //! Instructions available in the London hard fork. @@ -29,6 +32,41 @@ pub mod cancun { include!(concat!(env!("OUT_DIR"), "/cancun.rs")); } +/// Hard forks of the Ethereum Virtual Machine. +#[derive(Debug, Clone)] +pub enum HardFork { + /// The Cancun hard fork. + Cancun, + + /// The Shanghai hard fork. + Shanghai, + + /// The London hard fork. + London, + + /// The Prague hard fork. + Prague, +} + +impl Default for HardFork { + fn default() -> Self { + Self::Prague + } +} + +impl From<&OsStr> for HardFork { + fn from(s: &OsStr) -> Self { + let s = s.to_string_lossy().to_lowercase(); + match s.as_str() { + "cancun" => Self::Cancun, + "shanghai" => Self::Shanghai, + "london" => Self::London, + "prague" => Self::Prague, + _ => Self::default(), + } + } +} + /// Error that can occur when parsing an operation from a string. #[derive(Debug, Snafu)] pub struct FromStrError { From f3da1c6366ef1a24360c4122348944301f1ac64f Mon Sep 17 00:00:00 2001 From: Gaston Zanitti Date: Wed, 18 Oct 2023 12:27:28 -0300 Subject: [PATCH 33/73] Hardfork selection: firsts ideas --- etk-asm/src/asm.rs | 1 + etk-asm/src/ops.rs | 77 +++- etk-asm/tests/asm.rs | 21 +- etk-ops/build.rs | 91 ++-- etk-ops/src/lib.rs | 204 +++++++++ etk-ops/src/prague.toml | 921 ++++++++++++++++++++++++++++++++++++++++ 6 files changed, 1223 insertions(+), 92 deletions(-) create mode 100644 etk-ops/src/prague.toml diff --git a/etk-asm/src/asm.rs b/etk-asm/src/asm.rs index 3e5556db..6d685a9b 100644 --- a/etk-asm/src/asm.rs +++ b/etk-asm/src/asm.rs @@ -143,6 +143,7 @@ mod error { pub use self::error::Error; use crate::ops::expression::Error::{UndefinedVariable, UnknownLabel, UnknownMacro}; use crate::ops::{self, AbstractOp, Assemble, Expression, MacroDefinition}; +use etk_ops::HardFork; use rand::Rng; use std::cmp; use std::collections::{hash_map, HashMap}; diff --git a/etk-asm/src/ops.rs b/etk-asm/src/ops.rs index 1749459a..967008fe 100644 --- a/etk-asm/src/ops.rs +++ b/etk-asm/src/ops.rs @@ -2,7 +2,7 @@ mod error { use super::expression; - use etk_ops::cancun::Op; + use etk_ops::HardForkOp; use num_bigint::BigInt; use snafu::{Backtrace, Snafu}; @@ -16,7 +16,7 @@ mod error { ExpressionTooLarge { source: std::array::TryFromSliceError, value: BigInt, - spec: Op<()>, + spec: HardForkOp<()>, backtrace: Backtrace, }, ExpressionNegative { @@ -43,7 +43,9 @@ mod types; pub(crate) use self::error::Error; -use etk_ops::cancun::{Op, Operation, Push32}; +use etk_ops::cancun::Push32; +use etk_ops::HardForkOp; +use etk_ops::Operation; pub use self::error::UnknownSpecifierError; pub use self::expression::{Context, Expression, Terminal}; @@ -84,13 +86,13 @@ pub(crate) trait Concretize { fn concretize(&self, ctx: Context) -> Result; } -impl Concretize for Op { - type Concrete = Op<[u8]>; +impl Concretize for HardForkOp { + type Concrete = HardForkOp<[u8]>; fn concretize(&self, ctx: Context) -> Result { let expr = match self.immediate() { Some(i) => &i.tree, - None => return Ok(Op::new(self.code()).unwrap()), + None => return Ok(HardForkOp::new_op(self.code()).unwrap()), }; let value = expr @@ -110,13 +112,48 @@ impl Concretize for Op { bytes = new; } - let result = self - .code() - .with(bytes.as_slice()) - .context(error::ExpressionTooLarge { - value, - spec: self.code(), - })?; + let result = match &self { + HardForkOp::Cancun(op) => { + let op_context = + op.code() + .with(bytes.as_slice()) + .context(error::ExpressionTooLarge { + value, + spec: self.code(), + })?; + HardForkOp::Cancun(op_context) + } + HardForkOp::Shanghai(op) => { + let op_context = + op.code() + .with(bytes.as_slice()) + .context(error::ExpressionTooLarge { + value, + spec: self.code(), + })?; + HardForkOp::Shanghai(op_context) + } + HardForkOp::London(op) => { + let op_context = + op.code() + .with(bytes.as_slice()) + .context(error::ExpressionTooLarge { + value, + spec: self.code(), + })?; + HardForkOp::London(op_context) + } + HardForkOp::Prague(op) => { + let op_context = + op.code() + .with(bytes.as_slice()) + .context(error::ExpressionTooLarge { + value, + spec: self.code(), + })?; + HardForkOp::Prague(op_context) + } + }; Ok(result) } @@ -172,7 +209,7 @@ impl Access { #[derive(Debug, Clone, Eq, PartialEq)] pub enum AbstractOp { /// A real `Op`, as opposed to a label or variable sized push. - Op(Op), + Op(HardForkOp), /// A label, which is a virtual instruction. Label(String), @@ -191,12 +228,12 @@ impl AbstractOp { /// Construct a new `AbstractOp` from an `Operation`. pub fn new(op: O) -> Self where - O: Into>, + O: Into>, { Self::Op(op.into()) } - pub(crate) fn concretize(self, ctx: Context) -> Result, error::Error> { + pub(crate) fn concretize(self, ctx: Context) -> Result, error::Error> { match self { Self::Op(op) => op.concretize(ctx), Self::Push(imm) => { @@ -269,7 +306,7 @@ impl AbstractOp { } /// Return the specifier that corresponds to this `AbstractOp`. - pub fn specifier(&self) -> Option> { + pub fn specifier(&self) -> Option> { match self { Self::Op(op) => Some(op.code()), _ => None, @@ -277,12 +314,12 @@ impl AbstractOp { } } -impl From> for AbstractOp { - fn from(cop: Op<[u8]>) -> Self { +impl From> for AbstractOp { + fn from(cop: HardForkOp<[u8]>) -> Self { let code = cop.code(); let cop = match cop.into_immediate() { Some(i) => code.with(i).unwrap(), - None => Op::new(code).unwrap(), + None => HardForkOp::new_op(code).unwrap(), }; Self::Op(cop) } diff --git a/etk-asm/tests/asm.rs b/etk-asm/tests/asm.rs index c09dba9f..251e4007 100644 --- a/etk-asm/tests/asm.rs +++ b/etk-asm/tests/asm.rs @@ -2,6 +2,7 @@ use assert_matches::assert_matches; use etk_asm::ingest::{Error, Ingest}; +use etk_ops::HardFork; use hex_literal::hex; use std::path::{Path, PathBuf}; @@ -20,7 +21,7 @@ where #[test] fn simple_constructor() -> Result<(), Error> { let mut output = Vec::new(); - let mut ingester = Ingest::new(&mut output); + let mut ingester = Ingest::new(&mut output, HardFork::default()); ingester.ingest_file(source(&["simple-constructor", "ctor.etk"]))?; assert_eq!( @@ -39,7 +40,7 @@ fn simple_constructor() -> Result<(), Error> { #[test] fn out_of_bounds() { let mut output = Vec::new(); - let mut ingester = Ingest::new(&mut output); + let mut ingester = Ingest::new(&mut output, HardFork::default()); let err = ingester .ingest_file(source(&["out-of-bounds", "main", "main.etk"])) .unwrap_err(); @@ -50,7 +51,7 @@ fn out_of_bounds() { #[test] fn subdirectory() -> Result<(), Error> { let mut output = Vec::new(); - let mut ingester = Ingest::new(&mut output); + let mut ingester = Ingest::new(&mut output, HardFork::default()); ingester.ingest_file(source(&["subdirectory", "main.etk"]))?; assert_eq!(output, hex!("63c001c0de60ff")); @@ -61,7 +62,7 @@ fn subdirectory() -> Result<(), Error> { #[test] fn variable_jump() -> Result<(), Error> { let mut output = Vec::new(); - let mut ingester = Ingest::new(&mut output); + let mut ingester = Ingest::new(&mut output, HardFork::default()); ingester.ingest_file(source(&["variable-jump", "main.etk"]))?; assert_eq!(output, hex!("6003565b")); @@ -72,7 +73,7 @@ fn variable_jump() -> Result<(), Error> { #[test] fn instruction_macro() -> Result<(), Error> { let mut output = Vec::new(); - let mut ingester = Ingest::new(&mut output); + let mut ingester = Ingest::new(&mut output, HardFork::default()); ingester.ingest_file(source(&["instruction-macro", "main.etk"]))?; assert_eq!( @@ -86,7 +87,7 @@ fn instruction_macro() -> Result<(), Error> { #[test] fn instruction_macro_with_empty_lines() -> Result<(), Error> { let mut output = Vec::new(); - let mut ingester = Ingest::new(&mut output); + let mut ingester = Ingest::new(&mut output, HardFork::default()); ingester.ingest_file(source(&["instruction-macro", "empty_lines.etk"]))?; assert_eq!(output, hex!("6000600060006000600060006000")); @@ -97,7 +98,7 @@ fn instruction_macro_with_empty_lines() -> Result<(), Error> { #[test] fn instruction_macro_with_two_instructions_per_line() { let mut output = Vec::new(); - let mut ingester = Ingest::new(&mut output); + let mut ingester = Ingest::new(&mut output, HardFork::default()); let err = ingester .ingest_file(source(&[ "instruction-macro", @@ -111,7 +112,7 @@ fn instruction_macro_with_two_instructions_per_line() { #[test] fn undefined_label_undefined_macro() { let mut output = Vec::new(); - let mut ingester = Ingest::new(&mut output); + let mut ingester = Ingest::new(&mut output, HardFork::default()); let err = ingester .ingest_file(source(&[ "instruction-macro", @@ -127,7 +128,7 @@ fn undefined_label_undefined_macro() { #[test] fn every_op() -> Result<(), Error> { let mut output = Vec::new(); - let mut ingester = Ingest::new(&mut output); + let mut ingester = Ingest::new(&mut output, HardFork::default()); ingester.ingest_file(source(&["every-op", "main.etk"]))?; assert_eq!( @@ -296,7 +297,7 @@ fn every_op() -> Result<(), Error> { #[test] fn test_dynamic_push_and_include() -> Result<(), Error> { let mut output = Vec::new(); - let mut ingester = Ingest::new(&mut output); + let mut ingester = Ingest::new(&mut output, HardFork::default()); ingester.ingest_file(source(&["variable-push", "main.etk"]))?; assert_eq!(output, hex!("61025758585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585801010101010158585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858015b58")); diff --git a/etk-ops/build.rs b/etk-ops/build.rs index 97699974..3976e42a 100644 --- a/etk-ops/build.rs +++ b/etk-ops/build.rs @@ -96,64 +96,7 @@ fn read_fork(name: &str) -> Result<[(String, Op); 256], Error> { fn generate_fork(fork_name: &str) -> Result<(), Error> { let ops = read_fork(fork_name)?; - let mut tokens = quote! { - /// Trait for types that represent an EVM instruction. - pub trait Operation { - /// The return type of [`Operation::code`]. - type Code: Operation + Into; - - /// The return root type of [`Operation::immediate_mut`] and - /// [`Operation::immediate`]. - type ImmediateRef: ?Sized; - - /// The type of the immediate argument for this operation. - type Immediate: - std::borrow::Borrow + std::borrow::BorrowMut; - - /// Get a shared reference to the immediate argument of this operation, - /// if one exists. - fn immediate(&self) -> Option<&Self::ImmediateRef>; - - /// Get a mutable reference to the immediate argument of this operation, - /// if one exists. - fn immediate_mut(&mut self) -> Option<&mut Self::ImmediateRef>; - - /// Consume this operation and return its immediate argument, if one - /// exists. - fn into_immediate(self) -> Option; - - /// Length of immediate argument. - fn extra_len(&self) -> usize; - - /// The action (opcode) of this operation, without any immediates. - fn code(&self) -> Self::Code; - - /// The byte (opcode) that indicates this operation. - fn code_byte(&self) -> u8 { - self.code().into() - } - - /// Human-readable name for this operation. - fn mnemonic(&self) -> &str; - - /// Returns true if the current instruction changes the program counter (other - /// than incrementing it.) - fn is_jump(&self) -> bool; - - /// Returns true if the current instruction is a valid destination for jumps. - fn is_jump_target(&self) -> bool; - - /// Returns true if the current instruction causes the EVM to stop executing - /// the contract. - fn is_exit(&self) -> bool; - - /// How many stack elements this instruction pops. - fn pops(&self) -> usize; - - /// How many stack elements this instruction pushes. - fn pushes(&self) -> usize; - } - }; + let mut tokens = quote! {use super::Operation;}; let mut code_matches = quote! {}; let mut size_matches = quote! {}; @@ -409,8 +352,8 @@ fn generate_fork(fork_name: &str) -> Result<(), Error> { bounds.push(quote! { #ident }); } - let debug_bound = debug_bound.to_string(); - let clone_bound = clone_bound.to_string(); + //let debug_bound = debug_bound.to_string(); + //let clone_bound = clone_bound.to_string(); let partial_eq_bound = partial_eq_bound.to_string(); let eq_bound = eq_bound.to_string(); let ord_bound = ord_bound.to_string(); @@ -421,8 +364,6 @@ fn generate_fork(fork_name: &str) -> Result<(), Error> { #[doc = concat!("All instructions in the ", #fork_name, " fork.")] #[derive(educe::Educe)] #[educe( - Clone(bound = #clone_bound), - Debug(bound = #debug_bound), PartialEq(bound = #partial_eq_bound), Eq(bound = #eq_bound), Ord(bound = #ord_bound), @@ -441,6 +382,31 @@ fn generate_fork(fork_name: &str) -> Result<(), Error> { { } + // TODO: For some reason deriving Debug with educe didn't work. + use std::fmt; + impl fmt::Debug for Op + where + T: super::Immediates + ?Sized, + { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Op variant") + } + } + + // TODO: For some reason deriving Clone with educe didn't work. + impl Clone for Op + where + T: super::Immediates + ?Sized, + { + fn clone(&self) -> Self { + match self { + #( + Self::#names(n) => Self::#names(n), + )* + } + } + } + impl Operation for Op where T: super::Immediates + ?Sized { type Code = Op<()>; type Immediate = T::Immediate; @@ -852,4 +818,5 @@ fn main() { generate_fork("london").unwrap(); generate_fork("shanghai").unwrap(); generate_fork("cancun").unwrap(); + generate_fork("prague").unwrap(); } diff --git a/etk-ops/src/lib.rs b/etk-ops/src/lib.rs index 33e2bf15..50b602e9 100644 --- a/etk-ops/src/lib.rs +++ b/etk-ops/src/lib.rs @@ -17,6 +17,63 @@ use std::{ ffi::OsStr, }; +/// Trait for types that represent an EVM instruction. +pub trait Operation { + /// The return type of [`Operation::code`]. + type Code: Operation + Into; + + /// The return root type of [`Operation::immediate_mut`] and + /// [`Operation::immediate`]. + type ImmediateRef: ?Sized; + + /// The type of the immediate argument for this operation. + type Immediate: std::borrow::Borrow + + std::borrow::BorrowMut; + + /// Get a shared reference to the immediate argument of this operation, + /// if one exists. + fn immediate(&self) -> Option<&Self::ImmediateRef>; + + /// Get a mutable reference to the immediate argument of this operation, + /// if one exists. + fn immediate_mut(&mut self) -> Option<&mut Self::ImmediateRef>; + + /// Consume this operation and return its immediate argument, if one + /// exists. + fn into_immediate(self) -> Option; + + /// Length of immediate argument. + fn extra_len(&self) -> usize; + + /// The action (opcode) of this operation, without any immediates. + fn code(&self) -> Self::Code; + + /// The byte (opcode) that indicates this operation. + fn code_byte(&self) -> u8 { + self.code().into() + } + + /// Human-readable name for this operation. + fn mnemonic(&self) -> &str; + + /// Returns true if the current instruction changes the program counter (other + /// than incrementing it.) + fn is_jump(&self) -> bool; + + /// Returns true if the current instruction is a valid destination for jumps. + fn is_jump_target(&self) -> bool; + + /// Returns true if the current instruction causes the EVM to stop executing + /// the contract. + fn is_exit(&self) -> bool; + + /// How many stack elements this instruction pops. + fn pops(&self) -> usize; + + /// How many stack elements this instruction pushes. + fn pushes(&self) -> usize; +} + pub mod london { //! Instructions available in the London hard fork. include!(concat!(env!("OUT_DIR"), "/london.rs")); @@ -32,6 +89,153 @@ pub mod cancun { include!(concat!(env!("OUT_DIR"), "/cancun.rs")); } +pub mod prague { + //! Instructions available in the Prague hard fork. + include!(concat!(env!("OUT_DIR"), "/prague.rs")); +} + +/// An operation that can be executed by the EVM. +#[derive(Debug, Clone)] +pub enum HardForkOp +where + T: Immediates + ?Sized, +{ + /// Cancun hard fork operations. + Cancun(cancun::Op), + + /// Shanghai hard fork operations. + Shanghai(shanghai::Op), + + /// London hard fork operations. + London(london::Op), + + /// Prague hard fork operations. + Prague(prague::Op), +} + +impl HardForkOp +where + T: Immediates + ?Sized, +{ + /// Get an operation from a [`HardForkOp`] enum. + pub fn new_op(code: HardForkOp<()>) -> Option> { + match code { + HardForkOp::Cancun(op) => cancun::Op::new(op).map(HardForkOp::Cancun), + HardForkOp::Shanghai(op) => shanghai::Op::new(op).map(HardForkOp::Shanghai), + HardForkOp::London(op) => london::Op::new(op).map(HardForkOp::London), + HardForkOp::Prague(op) => prague::Op::new(op).map(HardForkOp::Prague), + } + } +} + +impl Operation for HardForkOp +where + T: Immediates + ?Sized, +{ + type Code = HardForkOp<()>; + type Immediate = T::Immediate; + type ImmediateRef = T::ImmediateRef; + fn immediate(&self) -> Option<&Self::ImmediateRef> { + match self { + HardForkOp::Cancun(op) => op.immediate(), + HardForkOp::Shanghai(op) => op.immediate(), + HardForkOp::London(op) => op.immediate(), + HardForkOp::Prague(op) => op.immediate(), + } + } + fn immediate_mut(&mut self) -> Option<&mut Self::ImmediateRef> { + match self { + HardForkOp::Cancun(op) => op.immediate_mut(), + HardForkOp::Shanghai(op) => op.immediate_mut(), + HardForkOp::London(op) => op.immediate_mut(), + HardForkOp::Prague(op) => op.immediate_mut(), + } + } + fn into_immediate(self) -> Option { + match self { + HardForkOp::Cancun(op) => op.into_immediate(), + HardForkOp::Shanghai(op) => op.into_immediate(), + HardForkOp::London(op) => op.into_immediate(), + HardForkOp::Prague(op) => op.into_immediate(), + } + } + fn extra_len(&self) -> usize { + match self { + HardForkOp::Cancun(op) => op.extra_len(), + HardForkOp::Shanghai(op) => op.extra_len(), + HardForkOp::London(op) => op.extra_len(), + HardForkOp::Prague(op) => op.extra_len(), + } + } + fn code(&self) -> Self::Code { + match self { + HardForkOp::Cancun(op) => HardForkOp::Cancun(op.code()), + HardForkOp::Shanghai(op) => HardForkOp::Shanghai(op.code()), + HardForkOp::London(op) => HardForkOp::London(op.code()), + HardForkOp::Prague(op) => HardForkOp::Prague(op.code()), + } + } + fn mnemonic(&self) -> &str { + match self { + HardForkOp::Cancun(op) => op.mnemonic(), + HardForkOp::Shanghai(op) => op.mnemonic(), + HardForkOp::London(op) => op.mnemonic(), + HardForkOp::Prague(op) => op.mnemonic(), + } + } + fn is_jump(&self) -> bool { + match self { + HardForkOp::Cancun(op) => op.is_jump(), + HardForkOp::Shanghai(op) => op.is_jump(), + HardForkOp::London(op) => op.is_jump(), + HardForkOp::Prague(op) => op.is_jump(), + } + } + fn is_jump_target(&self) -> bool { + match self { + HardForkOp::Cancun(op) => op.is_jump_target(), + HardForkOp::Shanghai(op) => op.is_jump_target(), + HardForkOp::London(op) => op.is_jump_target(), + HardForkOp::Prague(op) => op.is_jump_target(), + } + } + fn is_exit(&self) -> bool { + match self { + HardForkOp::Cancun(op) => op.is_exit(), + HardForkOp::Shanghai(op) => op.is_exit(), + HardForkOp::London(op) => op.is_exit(), + HardForkOp::Prague(op) => op.is_exit(), + } + } + fn pops(&self) -> usize { + match self { + HardForkOp::Cancun(op) => op.pops(), + HardForkOp::Shanghai(op) => op.pops(), + HardForkOp::London(op) => op.pops(), + HardForkOp::Prague(op) => op.pops(), + } + } + fn pushes(&self) -> usize { + match self { + HardForkOp::Cancun(op) => op.pushes(), + HardForkOp::Shanghai(op) => op.pushes(), + HardForkOp::London(op) => op.pushes(), + HardForkOp::Prague(op) => op.pushes(), + } + } +} + +impl From> for u8 { + fn from(op: HardForkOp<()>) -> u8 { + match op { + HardForkOp::Cancun(op) => op.code_byte(), + HardForkOp::Shanghai(op) => op.code_byte(), + HardForkOp::London(op) => op.code_byte(), + HardForkOp::Prague(op) => op.code_byte(), + } + } +} + /// Hard forks of the Ethereum Virtual Machine. #[derive(Debug, Clone)] pub enum HardFork { diff --git a/etk-ops/src/prague.toml b/etk-ops/src/prague.toml new file mode 100644 index 00000000..4d95eeac --- /dev/null +++ b/etk-ops/src/prague.toml @@ -0,0 +1,921 @@ +[Stop] +code = 0x00 +mnemonic = "stop" +pushes = 0 +pops = 0 +exits = true + +[Add] +code = 0x01 +mnemonic = "add" +pushes = 1 +pops = 2 + +[Mul] +code = 0x02 +mnemonic = "mul" +pushes = 1 +pops = 2 + +[Sub] +code = 0x03 +mnemonic = "sub" +pushes = 1 +pops = 2 + +[Div] +code = 0x04 +mnemonic = "div" +pushes = 1 +pops = 2 + +[SDiv] +code = 0x05 +mnemonic = "sdiv" +pushes = 1 +pops = 2 + +[Mod] +code = 0x06 +mnemonic = "mod" +pushes = 1 +pops = 2 + +[SMod] +code = 0x07 +mnemonic = "smod" +pushes = 1 +pops = 2 + +[AddMod] +code = 0x08 +mnemonic = "addmod" +pushes = 1 +pops = 3 + +[MulMod] +code = 0x09 +mnemonic = "mulmod" +pushes = 1 +pops = 3 + +[Exp] +code = 0x0a +mnemonic = "exp" +pushes = 1 +pops = 2 + +[SignExtend] +code = 0x0b +mnemonic = "signextend" +pushes = 1 +pops = 2 + +[Lt] +code = 0x10 +mnemonic = "lt" +pushes = 1 +pops = 2 + +[Gt] +code = 0x11 +mnemonic = "gt" +pushes = 1 +pops = 2 + +[SLt] +code = 0x12 +mnemonic = "slt" +pushes = 1 +pops = 2 + +[SGt] +code = 0x13 +mnemonic = "sgt" +pushes = 1 +pops = 2 + +[Eq] +code = 0x14 +mnemonic = "eq" +pushes = 1 +pops = 2 + +[IsZero] +code = 0x15 +mnemonic = "iszero" +pushes = 1 +pops = 1 + +[And] +code = 0x16 +mnemonic = "and" +pushes = 1 +pops = 2 + +[Or] +code = 0x17 +mnemonic = "or" +pushes = 1 +pops = 2 + +[Xor] +code = 0x18 +mnemonic = "xor" +pushes = 1 +pops = 2 + +[Not] +code = 0x19 +mnemonic = "not" +pushes = 1 +pops = 1 + +[Byte] +code = 0x1a +mnemonic = "byte" +pushes = 1 +pops = 2 + +[Shl] +code = 0x1b +mnemonic = "shl" +pushes = 1 +pops = 2 + +[Shr] +code = 0x1c +mnemonic = "shr" +pushes = 1 +pops = 2 + +[Sar] +code = 0x1d +mnemonic = "sar" +pushes = 1 +pops = 2 + +[Keccak256] +code = 0x20 +mnemonic = "keccak256" +pushes = 1 +pops = 2 + +[Address] +code = 0x30 +mnemonic = "address" +pushes = 1 +pops = 0 + +[Balance] +code = 0x31 +mnemonic = "balance" +pushes = 1 +pops = 1 + +[Origin] +code = 0x32 +mnemonic = "origin" +pushes = 1 +pops = 0 + +[Caller] +code = 0x33 +mnemonic = "caller" +pushes = 1 +pops = 0 + +[CallValue] +code = 0x34 +mnemonic = "callvalue" +pushes = 1 +pops = 0 + +[CallDataLoad] +code = 0x35 +mnemonic = "calldataload" +pushes = 1 +pops = 1 + +[CallDataSize] +code = 0x36 +mnemonic = "calldatasize" +pushes = 1 +pops = 0 + +[CallDataCopy] +code = 0x37 +mnemonic = "calldatacopy" +pushes = 0 +pops = 3 + +[CodeSize] +code = 0x38 +mnemonic = "codesize" +pushes = 1 +pops = 0 + +[CodeCopy] +code = 0x39 +mnemonic = "codecopy" +pushes = 0 +pops = 3 + +[GasPrice] +code = 0x3a +mnemonic = "gasprice" +pushes = 1 +pops = 0 + +[ExtCodeSize] +code = 0x3b +mnemonic = "extcodesize" +pushes = 1 +pops = 1 + +[ExtCodeCopy] +code = 0x3c +mnemonic = "extcodecopy" +pushes = 0 +pops = 4 + +[ReturnDataSize] +code = 0x3d +mnemonic = "returndatasize" +pushes = 1 +pops = 0 + +[ReturnDataCopy] +code = 0x3e +mnemonic = "returndatacopy" +pushes = 0 +pops = 3 + +[ExtCodeHash] +code = 0x3f +mnemonic = "extcodehash" +pushes = 1 +pops = 1 + +[BlockHash] +code = 0x40 +mnemonic = "blockhash" +pushes = 1 +pops = 1 + +[Coinbase] +code = 0x41 +mnemonic = "coinbase" +pushes = 1 +pops = 0 + +[Timestamp] +code = 0x42 +mnemonic = "timestamp" +pushes = 1 +pops = 0 + +[Number] +code = 0x43 +mnemonic = "number" +pushes = 1 +pops = 0 + +[Difficulty] +code = 0x44 +mnemonic = "difficulty" +pushes = 1 +pops = 0 + +[GasLimit] +code = 0x45 +mnemonic = "gaslimit" +pushes = 1 +pops = 0 + +[ChainId] +code = 0x46 +mnemonic = "chainid" +pushes = 1 +pops = 0 + +[SelfBalance] +code = 0x47 +mnemonic = "selfbalance" +pushes = 1 +pops = 0 + +[BaseFee] +code = 0x48 +mnemonic = "basefee" +pushes = 1 +pops = 0 + +[Pop] +code = 0x50 +mnemonic = "pop" +pushes = 0 +pops = 1 + +[MLoad] +code = 0x51 +mnemonic = "mload" +pushes = 1 +pops = 1 + +[MStore] +code = 0x52 +mnemonic = "mstore" +pushes = 0 +pops = 2 + +[MStore8] +code = 0x53 +mnemonic = "mstore8" +pushes = 1 +pops = 2 + +[SLoad] +code = 0x54 +mnemonic = "sload" +pushes = 1 +pops = 1 + +[SStore] +code = 0x55 +mnemonic = "sstore" +pushes = 0 +pops = 2 + +[Jump] +code = 0x56 +mnemonic = "jump" +pushes = 0 +pops = 1 +jump = true + +[JumpI] +code = 0x57 +mnemonic = "jumpi" +pushes = 0 +pops = 2 +jump = true + +[GetPc] +code = 0x58 +mnemonic = "pc" +pushes = 1 +pops = 0 + +[MSize] +code = 0x59 +mnemonic = "msize" +pushes = 1 +pops = 0 + +[Gas] +code = 0x5a +mnemonic = "gas" +pushes = 1 +pops = 0 + +[Nop] +code = 0x5b +mnemonic = "nop" +pushes = 0 +pops = 0 + +[MCopy] +code = 0x5e +mnemonic = "mcopy" +pushes = 0 +pops = 3 + +[Push0] +code = 0x5f +mnemonic = "push0" +extra_len = 0 +pushes = 1 +pops = 0 + +[Push1] +code = 0x60 +mnemonic = "push1" +extra_len = 1 +pushes = 1 +pops = 0 + +[Push2] +code = 0x61 +mnemonic = "push2" +extra_len = 2 +pushes = 1 +pops = 0 + +[Push3] +code = 0x62 +mnemonic = "push3" +extra_len = 3 +pushes = 1 +pops = 0 + +[Push4] +code = 0x63 +mnemonic = "push4" +extra_len = 4 +pushes = 1 +pops = 0 + +[Push5] +code = 0x64 +mnemonic = "push5" +extra_len = 5 +pushes = 1 +pops = 0 + +[Push6] +code = 0x65 +mnemonic = "push6" +extra_len = 6 +pushes = 1 +pops = 0 + +[Push7] +code = 0x66 +mnemonic = "push7" +extra_len = 7 +pushes = 1 +pops = 0 + +[Push8] +code = 0x67 +mnemonic = "push8" +extra_len = 8 +pushes = 1 +pops = 0 + +[Push9] +code = 0x68 +mnemonic = "push9" +extra_len = 9 +pushes = 1 +pops = 0 + +[Push10] +code = 0x69 +mnemonic = "push10" +extra_len = 10 +pushes = 1 +pops = 0 + +[Push11] +code = 0x6a +mnemonic = "push11" +extra_len = 11 +pushes = 1 +pops = 0 + +[Push12] +code = 0x6b +mnemonic = "push12" +extra_len = 12 +pushes = 1 +pops = 0 + +[Push13] +code = 0x6c +mnemonic = "push13" +extra_len = 13 +pushes = 1 +pops = 0 + +[Push14] +code = 0x6d +mnemonic = "push14" +extra_len = 14 +pushes = 1 +pops = 0 + +[Push15] +code = 0x6e +mnemonic = "push15" +extra_len = 15 +pushes = 1 +pops = 0 + +[Push16] +code = 0x6f +mnemonic = "push16" +extra_len = 16 +pushes = 1 +pops = 0 + +[Push17] +code = 0x70 +mnemonic = "push17" +extra_len = 17 +pushes = 1 +pops = 0 + +[Push18] +code = 0x71 +mnemonic = "push18" +extra_len = 18 +pushes = 1 +pops = 0 + +[Push19] +code = 0x72 +mnemonic = "push19" +extra_len = 19 +pushes = 1 +pops = 0 + +[Push20] +code = 0x73 +mnemonic = "push20" +extra_len = 20 +pushes = 1 +pops = 0 + +[Push21] +code = 0x74 +mnemonic = "push21" +extra_len = 21 +pushes = 1 +pops = 0 + +[Push22] +code = 0x75 +mnemonic = "push22" +extra_len = 22 +pushes = 1 +pops = 0 + +[Push23] +code = 0x76 +mnemonic = "push23" +extra_len = 23 +pushes = 1 +pops = 0 + +[Push24] +code = 0x77 +mnemonic = "push24" +extra_len = 24 +pushes = 1 +pops = 0 + +[Push25] +code = 0x78 +mnemonic = "push25" +extra_len = 25 +pushes = 1 +pops = 0 + +[Push26] +code = 0x79 +mnemonic = "push26" +extra_len = 26 +pushes = 1 +pops = 0 + +[Push27] +code = 0x7a +mnemonic = "push27" +extra_len = 27 +pushes = 1 +pops = 0 + +[Push28] +code = 0x7b +mnemonic = "push28" +extra_len = 28 +pushes = 1 +pops = 0 + +[Push29] +code = 0x7c +mnemonic = "push29" +extra_len = 29 +pushes = 1 +pops = 0 + +[Push30] +code = 0x7d +mnemonic = "push30" +extra_len = 30 +pushes = 1 +pops = 0 + +[Push31] +code = 0x7e +mnemonic = "push31" +extra_len = 31 +pushes = 1 +pops = 0 + +[Push32] +code = 0x7f +mnemonic = "push32" +extra_len = 32 +pushes = 1 +pops = 0 + +[Dup1] +code = 0x80 +mnemonic = "dup1" +pushes = 2 +pops = 1 + +[Dup2] +code = 0x81 +mnemonic = "dup2" +pushes = 3 +pops = 2 + +[Dup3] +code = 0x82 +mnemonic = "dup3" +pushes = 4 +pops = 3 + +[Dup4] +code = 0x83 +mnemonic = "dup4" +pushes = 5 +pops = 4 + +[Dup5] +code = 0x84 +mnemonic = "dup5" +pushes = 6 +pops = 5 + +[Dup6] +code = 0x85 +mnemonic = "dup6" +pushes = 7 +pops = 6 + +[Dup7] +code = 0x86 +mnemonic = "dup7" +pushes = 8 +pops = 7 + +[Dup8] +code = 0x87 +mnemonic = "dup8" +pushes = 9 +pops = 8 + +[Dup9] +code = 0x88 +mnemonic = "dup9" +pushes = 10 +pops = 9 + +[Dup10] +code = 0x89 +mnemonic = "dup10" +pushes = 11 +pops = 10 + +[Dup11] +code = 0x8a +mnemonic = "dup11" +pushes = 12 +pops = 11 + +[Dup12] +code = 0x8b +mnemonic = "dup12" +pushes = 13 +pops = 12 + +[Dup13] +code = 0x8c +mnemonic = "dup13" +pushes = 14 +pops = 13 + +[Dup14] +code = 0x8d +mnemonic = "dup14" +pushes = 15 +pops = 14 + +[Dup15] +code = 0x8e +mnemonic = "dup15" +pushes = 16 +pops = 15 + +[Dup16] +code = 0x8f +mnemonic = "dup16" +pushes = 17 +pops = 16 + +[Swap1] +code = 0x90 +mnemonic = "swap1" +pushes = 2 +pops = 2 + +[Swap2] +code = 0x91 +mnemonic = "swap2" +pushes = 3 +pops = 3 + +[Swap3] +code = 0x92 +mnemonic = "swap3" +pushes = 4 +pops = 4 + +[Swap4] +code = 0x93 +mnemonic = "swap4" +pushes = 5 +pops = 5 + +[Swap5] +code = 0x94 +mnemonic = "swap5" +pushes = 6 +pops = 6 + +[Swap6] +code = 0x95 +mnemonic = "swap6" +pushes = 7 +pops = 7 + +[Swap7] +code = 0x96 +mnemonic = "swap7" +pushes = 8 +pops = 8 + +[Swap8] +code = 0x97 +mnemonic = "swap8" +pushes = 9 +pops = 9 + +[Swap9] +code = 0x98 +mnemonic = "swap9" +pushes = 10 +pops = 10 + +[Swap10] +code = 0x99 +mnemonic = "swap10" +pushes = 11 +pops = 11 + +[Swap11] +code = 0x9a +mnemonic = "swap11" +pushes = 12 +pops = 12 + +[Swap12] +code = 0x9b +mnemonic = "swap12" +pushes = 13 +pops = 13 + +[Swap13] +code = 0x9c +mnemonic = "swap13" +pushes = 14 +pops = 14 + +[Swap14] +code = 0x9d +mnemonic = "swap14" +pushes = 15 +pops = 15 + +[Swap15] +code = 0x9e +mnemonic = "swap15" +pushes = 16 +pops = 16 + +[Swap16] +code = 0x9f +mnemonic = "swap16" +pushes = 17 +pops = 17 + +[Log0] +code = 0xa0 +mnemonic = "log0" +pushes = 2 +pops = 2 + +[Log1] +code = 0xa1 +mnemonic = "log1" +pushes = 3 +pops = 3 + +[Log2] +code = 0xa2 +mnemonic = "log2" +pushes = 4 +pops = 4 + +[Log3] +code = 0xa3 +mnemonic = "log3" +pushes = 5 +pops = 5 + +[Log4] +code = 0xa4 +mnemonic = "log4" +pushes = 6 +pops = 6 + +[Callf] +code = 0xe3 +mnemonic = "callf" +extra_len = 16 +pushes = 0 +pops = 0 + +[Retf] +code = 0xe4 +mnemonic = "retf" +pushes = 0 +pops = 0 + +[Create] +code = 0xf0 +mnemonic = "create" +pushes = 1 +pops = 3 + +[Call] +code = 0xf1 +mnemonic = "call" +pushes = 1 +pops = 7 + +[CallCode] +code = 0xf2 +mnemonic = "callcode" +pushes = 1 +pops = 7 + +[Return] +code = 0xf3 +mnemonic = "return" +pushes = 0 +pops = 2 +exits = true + +[DelegateCall] +code = 0xf4 +mnemonic = "delegatecall" +pushes = 1 +pops = 6 + +[Create2] +code = 0xf5 +mnemonic = "create2" +pushes = 1 +pops = 4 + +[StaticCall] +code = 0xfa +mnemonic = "staticcall" +pushes = 1 +pops = 6 + +[Revert] +code = 0xfd +mnemonic = "revert" +pushes = 0 +pops = 2 +exits = true + +[Invalid] +code = 0xfe +mnemonic = "invalid" +pushes = 0 +pops = 0 +exits = true + +[SelfDestruct] +code = 0xff +mnemonic = "selfdestruct" +pushes = 0 +pops = 2 From 1bf12c825f444adc1ad36dd76570759e5696ee90 Mon Sep 17 00:00:00 2001 From: Gaston Zanitti Date: Thu, 19 Oct 2023 16:55:37 -0300 Subject: [PATCH 34/73] More progress towards Hardfork selection --- etk-asm/src/asm.rs | 303 +++++++++++++++++++++------------------ etk-asm/src/ast.rs | 29 +++- etk-asm/src/ops.rs | 47 +++++- etk-asm/src/parse/mod.rs | 1 - etk-ops/build.rs | 9 +- etk-ops/src/lib.rs | 203 +++++++++++++++++++++++++- 6 files changed, 439 insertions(+), 153 deletions(-) diff --git a/etk-asm/src/asm.rs b/etk-asm/src/asm.rs index 6d685a9b..0fbbb87f 100644 --- a/etk-asm/src/asm.rs +++ b/etk-asm/src/asm.rs @@ -6,7 +6,7 @@ mod error { use crate::ops::Expression; use crate::ParseError; - use etk_ops::cancun::Op; + use etk_ops::{cancun::Op, HardForkOp}; use num_bigint::BigInt; use snafu::{Backtrace, Snafu}; @@ -53,7 +53,7 @@ mod error { value: BigInt, /// The specifier. - spec: Op<()>, + spec: HardForkOp<()>, /// The location of the error. backtrace: Backtrace, @@ -187,9 +187,9 @@ impl From> for RawOp { /// # use etk_asm::asm::Error; /// # /// # use hex_literal::hex; -/// let mut asm = Assembler::new(); +/// let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); /// let result = asm.assemble(vec![ -/// AbstractOp::new(GetPc), +/// AbstractOp::new(HardForkOp::Cancun(GetPc.into())), /// ])?; /// # assert_eq!(result, hex!("58")); /// # Result::<(), Error>::Ok(()) @@ -211,6 +211,9 @@ pub struct Assembler { /// Labels that have been referred to (ex. with push) but /// have not been declared with an `AbstractOp::Label`. undeclared_labels: Vec, + + /// Hardfork to use when assembling. + hardfork: HardFork, } /// Struct used to keep track of pending label invocations and their positions in code. @@ -232,6 +235,13 @@ impl Assembler { Self::default() } + pub fn new_with_hardfork(hardfork: HardFork) -> Self { + Self { + hardfork, + ..Self::default() + } + } + /// Inspect the macros in a series of [`RawOp`] and declare them in the assembler. fn inspect_macros(&mut self, nodes: &I) -> Result<(), Error> where @@ -265,10 +275,10 @@ impl Assembler { let mut output = Vec::new(); for op in self.ready.iter() { if let RawOp::Op(ref op) = op { - match op - .clone() - .concretize((&self.declared_labels, &self.declared_macros).into()) - { + match op.clone().concretize( + (&self.declared_labels, &self.declared_macros).into(), + self.hardfork, + ) { Ok(cop) => cop.assemble(&mut output), Err(ops::Error::ContextIncomplete { source: UnknownLabel { label: _label, .. }, @@ -424,10 +434,10 @@ impl Assembler { RawOp::Op(AbstractOp::MacroDefinition(_)) => Ok(()), RawOp::Op(AbstractOp::Macro(_)) => Ok(()), RawOp::Op(ref op) => { - match op - .clone() - .concretize((&self.declared_labels, &self.declared_macros).into()) - { + match op.clone().concretize( + (&self.declared_labels, &self.declared_macros).into(), + self.hardfork, + ) { Ok(cop) => { self.concrete_len += cop.size(); self.ready.push(rop) @@ -566,15 +576,15 @@ mod tests { InstructionMacroDefinition, InstructionMacroInvocation, Terminal, }; use assert_matches::assert_matches; - use etk_ops::cancun::*; + use etk_ops::{cancun::*, HardForkOp}; use hex_literal::hex; use num_bigint::{BigInt, Sign}; #[test] fn assemble_variable_push_const_while_pending() -> Result<(), Error> { - let mut asm = Assembler::new(); + let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); let result = asm.assemble(vec![ - AbstractOp::Op(Push1(Imm::with_label("label1")).into()), + AbstractOp::Op(HardForkOp::Cancun(Push1(Imm::with_label("label1")).into())), AbstractOp::Push(Terminal::Number(0xaabb.into()).into()), AbstractOp::Label("label1".into()), ])?; @@ -584,15 +594,15 @@ mod tests { #[test] fn assemble_variable_pushes_abab() -> Result<(), Error> { - let mut asm = Assembler::new(); + let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); let result = asm.assemble(vec![ - AbstractOp::new(JumpDest), + AbstractOp::new(HardForkOp::Cancun(JumpDest.into())), AbstractOp::Push(Imm::with_label("label1")), AbstractOp::Push(Imm::with_label("label2")), AbstractOp::Label("label1".into()), - AbstractOp::new(GetPc), + AbstractOp::new(HardForkOp::Cancun(GetPc.into())), AbstractOp::Label("label2".into()), - AbstractOp::new(GetPc), + AbstractOp::new(HardForkOp::Cancun(GetPc.into())), ])?; assert_eq!(result, hex!("5b600560065858")); Ok(()) @@ -600,15 +610,15 @@ mod tests { #[test] fn assemble_variable_pushes_abba() -> Result<(), Error> { - let mut asm = Assembler::new(); + let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); let result = asm.assemble(vec![ - AbstractOp::new(JumpDest), + AbstractOp::new(HardForkOp::Cancun(JumpDest.into())), AbstractOp::Push(Imm::with_label("label1")), AbstractOp::Push(Imm::with_label("label2")), AbstractOp::Label("label2".into()), - AbstractOp::new(GetPc), + AbstractOp::new(HardForkOp::Cancun(GetPc.into())), AbstractOp::Label("label1".into()), - AbstractOp::new(GetPc), + AbstractOp::new(HardForkOp::Cancun(GetPc.into())), ])?; assert_eq!(result, hex!("5b600660055858")); Ok(()) @@ -616,9 +626,9 @@ mod tests { #[test] fn assemble_variable_push1_multiple() -> Result<(), Error> { - let mut asm = Assembler::new(); + let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); let result = asm.assemble(vec![ - AbstractOp::new(JumpDest), + AbstractOp::new(HardForkOp::Cancun(JumpDest.into())), AbstractOp::Push(Imm::with_label("auto")), AbstractOp::Push(Imm::with_label("auto")), AbstractOp::Label("auto".into()), @@ -629,7 +639,7 @@ mod tests { #[test] fn assemble_variable_push_const() -> Result<(), Error> { - let mut asm = Assembler::new(); + let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); let result = asm.assemble(vec![AbstractOp::Push( Terminal::Number((0x00aaaaaaaaaaaaaaaaaaaaaaaa as u128).into()).into(), )])?; @@ -641,7 +651,7 @@ mod tests { fn assemble_variable_push_too_large() { let v = BigInt::from_bytes_be(Sign::Plus, &[1u8; 33]); - let mut asm = Assembler::new(); + let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); let err = asm .assemble(vec![AbstractOp::Push(Terminal::Number(v).into())]) .unwrap_err(); @@ -651,7 +661,7 @@ mod tests { #[test] fn assemble_variable_push_negative() { - let mut asm = Assembler::new(); + let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); let err = asm .assemble(vec![AbstractOp::Push(Terminal::Number((-1).into()).into())]) .unwrap_err(); @@ -661,7 +671,7 @@ mod tests { #[test] fn assemble_variable_push_const0() -> Result<(), Error> { - let mut asm = Assembler::new(); + let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); let result = asm.assemble(vec![AbstractOp::Push( Terminal::Number((0x00 as u128).into()).into(), )])?; @@ -671,9 +681,9 @@ mod tests { #[test] fn assemble_variable_push1_known() -> Result<(), Error> { - let mut asm = Assembler::new(); + let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); let result = asm.assemble(vec![ - AbstractOp::new(JumpDest), + AbstractOp::new(HardForkOp::Cancun(JumpDest.into())), AbstractOp::Label("auto".into()), AbstractOp::Push(Imm::with_label("auto")), ])?; @@ -683,11 +693,11 @@ mod tests { #[test] fn assemble_variable_push1() -> Result<(), Error> { - let mut asm = Assembler::new(); + let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); let result = asm.assemble(vec![ AbstractOp::Push(Imm::with_label("auto")), AbstractOp::Label("auto".into()), - AbstractOp::new(JumpDest), + AbstractOp::new(HardForkOp::Cancun(JumpDest.into())), ])?; assert_eq!(result, hex!("60025b")); Ok(()) @@ -695,12 +705,12 @@ mod tests { #[test] fn assemble_variable_push1_reuse() -> Result<(), Error> { - let mut asm = Assembler::new(); + let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); let result = asm.assemble(vec![ AbstractOp::Push(Imm::with_label("auto")), AbstractOp::Label("auto".into()), - AbstractOp::new(JumpDest), - AbstractOp::new(Push1(Imm::with_label("auto"))), + AbstractOp::new(HardForkOp::Cancun(JumpDest.into())), + AbstractOp::new(HardForkOp::Cancun(Push1(Imm::with_label("auto")).into())), ])?; assert_eq!(result, hex!("60025b6002")); Ok(()) @@ -711,13 +721,13 @@ mod tests { let mut code = vec![]; code.push(AbstractOp::Push(Imm::with_label("auto"))); for _ in 0..255 { - code.push(AbstractOp::new(GetPc)); + code.push(AbstractOp::new(HardForkOp::Cancun(GetPc.into()))); } code.push(AbstractOp::Label("auto".into())); - code.push(AbstractOp::new(JumpDest)); + code.push(AbstractOp::new(HardForkOp::Cancun(JumpDest.into()))); - let mut asm = Assembler::new(); + let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); let result = asm.assemble(code)?; let mut expected = vec![0x61, 0x01, 0x02]; @@ -730,9 +740,11 @@ mod tests { #[test] fn assemble_undeclared_label() -> Result<(), Error> { - let mut asm = Assembler::new(); + let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); let err = asm - .assemble(vec![AbstractOp::new(Push1(Imm::with_label("hi")))]) + .assemble(vec![AbstractOp::new(HardForkOp::Cancun( + Push1(Imm::with_label("hi")).into(), + ))]) .unwrap_err(); assert_matches!(err, Error::UndeclaredLabels { labels, .. } if labels == vec!["hi"]); Ok(()) @@ -740,8 +752,8 @@ mod tests { #[test] fn assemble_jumpdest_no_label() -> Result<(), Error> { - let mut asm = Assembler::new(); - let result = asm.assemble(vec![AbstractOp::new(JumpDest)])?; + let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); + let result = asm.assemble(vec![AbstractOp::new(HardForkOp::Cancun(JumpDest.into()))])?; assert!(asm.declared_labels.is_empty()); assert_eq!(result, hex!("5b")); Ok(()) @@ -749,8 +761,11 @@ mod tests { #[test] fn assemble_jumpdest_with_label() -> Result<(), Error> { - let mut asm = Assembler::new(); - let ops = vec![AbstractOp::Label("lbl".into()), AbstractOp::new(JumpDest)]; + let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); + let ops = vec![ + AbstractOp::Label("lbl".into()), + AbstractOp::new(HardForkOp::Cancun(JumpDest.into())), + ]; let result = asm.assemble(ops)?; assert_eq!(asm.declared_labels.len(), 1); @@ -763,11 +778,11 @@ mod tests { fn assemble_jumpdest_jump_with_label() -> Result<(), Error> { let ops = vec![ AbstractOp::Label("lbl".into()), - AbstractOp::new(JumpDest), - AbstractOp::new(Push1(Imm::with_label("lbl"))), + AbstractOp::new(HardForkOp::Cancun(JumpDest.into())), + AbstractOp::new(HardForkOp::Cancun(Push1(Imm::with_label("lbl")).into())), ]; - let mut asm = Assembler::new(); + let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); let result = asm.assemble(ops)?; assert_eq!(result, hex!("5b6000")); @@ -777,12 +792,12 @@ mod tests { #[test] fn assemble_labeled_pc() -> Result<(), Error> { let ops = vec![ - AbstractOp::new(Push1(Imm::with_label("lbl"))), + AbstractOp::new(HardForkOp::Cancun(Push1(Imm::with_label("lbl")).into())), AbstractOp::Label("lbl".into()), - AbstractOp::new(GetPc), + AbstractOp::new(HardForkOp::Cancun(GetPc.into())), ]; - let mut asm = Assembler::new(); + let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); let result = asm.assemble(ops)?; assert_eq!(result, hex!("600258")); @@ -792,12 +807,12 @@ mod tests { #[test] fn assemble_jump_jumpdest_with_label() -> Result<(), Error> { let ops = vec![ - AbstractOp::new(Push1(Imm::with_label("lbl"))), + AbstractOp::new(HardForkOp::Cancun(Push1(Imm::with_label("lbl")).into())), AbstractOp::Label("lbl".into()), - AbstractOp::new(JumpDest), + AbstractOp::new(HardForkOp::Cancun(JumpDest.into())), ]; - let mut asm = Assembler::new(); + let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); let result = asm.assemble(ops)?; assert_eq!(result, hex!("60025b")); @@ -806,26 +821,30 @@ mod tests { #[test] fn assemble_label_too_large() { - let mut ops: Vec<_> = vec![AbstractOp::new(GetPc); 255]; + let mut ops: Vec<_> = vec![AbstractOp::new(HardForkOp::Cancun(GetPc.into())); 255]; ops.push(AbstractOp::Label("b".into())); - ops.push(AbstractOp::new(JumpDest)); + ops.push(AbstractOp::new(HardForkOp::Cancun(JumpDest.into()))); ops.push(AbstractOp::Label("a".into())); - ops.push(AbstractOp::new(JumpDest)); - ops.push(AbstractOp::new(Push1(Imm::with_label("a")))); - let mut asm = Assembler::new(); + ops.push(AbstractOp::new(HardForkOp::Cancun(JumpDest.into()))); + ops.push(AbstractOp::new(HardForkOp::Cancun( + Push1(Imm::with_label("a")).into(), + ))); + let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); let err = asm.assemble(ops).unwrap_err(); assert_matches!(err, Error::ExpressionTooLarge { expr: Expression::Terminal(Terminal::Label(label)), .. } if label == "a"); } #[test] fn assemble_label_just_right() -> Result<(), Error> { - let mut ops: Vec<_> = vec![AbstractOp::new(GetPc); 255]; + let mut ops: Vec<_> = vec![AbstractOp::new(HardForkOp::Cancun(GetPc.into())); 255]; ops.push(AbstractOp::Label("b".into())); - ops.push(AbstractOp::new(JumpDest)); + ops.push(AbstractOp::new(HardForkOp::Cancun(JumpDest.into()))); ops.push(AbstractOp::Label("a".into())); - ops.push(AbstractOp::new(JumpDest)); - ops.push(AbstractOp::new(Push1(Imm::with_label("b")))); - let mut asm = Assembler::new(); + ops.push(AbstractOp::new(HardForkOp::Cancun(JumpDest.into()))); + ops.push(AbstractOp::new(HardForkOp::Cancun( + Push1(Imm::with_label("b")).into(), + ))); + let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); let result = asm.assemble(ops)?; let mut expected = vec![0x58; 255]; @@ -864,7 +883,7 @@ mod tests { }), ]; - let mut asm = Assembler::new(); + let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); let result = asm.assemble(ops)?; assert_eq!(result, []); @@ -879,15 +898,15 @@ mod tests { parameters: vec![], contents: vec![ AbstractOp::Label("a".into()), - AbstractOp::new(JumpDest), - AbstractOp::new(Push1(Imm::with_label("a"))), - AbstractOp::new(Push1(Imm::with_label("b"))), + AbstractOp::new(HardForkOp::Cancun(JumpDest.into())), + AbstractOp::new(HardForkOp::Cancun(Push1(Imm::with_label("a")).into())), + AbstractOp::new(HardForkOp::Cancun(Push1(Imm::with_label("b")).into())), ], } .into(), AbstractOp::Label("b".into()), - AbstractOp::new(JumpDest), - AbstractOp::new(Push1(Imm::with_label("b"))), + AbstractOp::new(HardForkOp::Cancun(JumpDest.into())), + AbstractOp::new(HardForkOp::Cancun(Push1(Imm::with_label("b")).into())), AbstractOp::Macro(InstructionMacroInvocation { name: "my_macro".into(), parameters: vec![], @@ -898,7 +917,7 @@ mod tests { }), ]; - let mut asm = Assembler::new(); + let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); let result = asm.assemble(ops)?; assert_eq!(result, hex!("5b60005b600360005b60086000")); @@ -913,22 +932,22 @@ mod tests { parameters: vec![], contents: vec![ AbstractOp::Label("a".into()), - AbstractOp::new(JumpDest), - AbstractOp::new(Push1(Imm::with_label("a"))), - AbstractOp::new(Push1(Imm::with_label("b"))), + AbstractOp::new(HardForkOp::Cancun(JumpDest.into())), + AbstractOp::new(HardForkOp::Cancun(Push1(Imm::with_label("a")).into())), + AbstractOp::new(HardForkOp::Cancun(Push1(Imm::with_label("b")).into())), ], } .into(), AbstractOp::Label("b".into()), - AbstractOp::new(JumpDest), - AbstractOp::new(Push1(Imm::with_label("b"))), + AbstractOp::new(HardForkOp::Cancun(JumpDest.into())), + AbstractOp::new(HardForkOp::Cancun(Push1(Imm::with_label("b")).into())), AbstractOp::Macro(InstructionMacroInvocation { name: "my_macro".into(), parameters: vec![], }), ]; - let mut asm = Assembler::new(); + let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); let result = asm.assemble(ops)?; assert_eq!(result, hex!("5b60005b60036000")); @@ -939,8 +958,8 @@ mod tests { fn assemble_instruction_macro_delayed_definition() -> Result<(), Error> { let ops = vec![ AbstractOp::Label("b".into()), - AbstractOp::new(JumpDest), - AbstractOp::new(Push1(Imm::with_label("b"))), + AbstractOp::new(HardForkOp::Cancun(JumpDest.into())), + AbstractOp::new(HardForkOp::Cancun(Push1(Imm::with_label("b")).into())), AbstractOp::Macro(InstructionMacroInvocation { name: "my_macro".into(), parameters: vec![], @@ -950,15 +969,15 @@ mod tests { parameters: vec![], contents: vec![ AbstractOp::Label("a".into()), - AbstractOp::new(JumpDest), - AbstractOp::new(Push1(Imm::with_label("a"))), - AbstractOp::new(Push1(Imm::with_label("b"))), + AbstractOp::new(HardForkOp::Cancun(JumpDest.into())), + AbstractOp::new(HardForkOp::Cancun(Push1(Imm::with_label("a")).into())), + AbstractOp::new(HardForkOp::Cancun(Push1(Imm::with_label("b")).into())), ], } .into(), ]; - let mut asm = Assembler::new(); + let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); let result = asm.assemble(ops)?; assert_eq!(result, hex!("5b60005b60036000")); @@ -976,19 +995,19 @@ mod tests { name: "my_macro".into(), parameters: vec![], contents: vec![ - AbstractOp::new(JumpDest), + AbstractOp::new(HardForkOp::Cancun(JumpDest.into())), AbstractOp::Push(Imm::with_label("label1")), AbstractOp::Push(Imm::with_label("label2")), AbstractOp::Label("label1".into()), - AbstractOp::new(GetPc), + AbstractOp::new(HardForkOp::Cancun(GetPc.into())), AbstractOp::Label("label2".into()), - AbstractOp::new(GetPc), + AbstractOp::new(HardForkOp::Cancun(GetPc.into())), ], } .into(), ]; - let mut asm = Assembler::new(); + let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); let result = asm.assemble(ops)?; assert_eq!(result, hex!("5b600560065858")); @@ -1000,7 +1019,7 @@ mod tests { let ops = vec![AbstractOp::Macro( InstructionMacroInvocation::with_zero_parameters("my_macro".into()), )]; - let mut asm = Assembler::new(); + let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); let err = asm.assemble(ops).unwrap_err(); assert_matches!(err, Error::UndeclaredInstructionMacro { name, .. } if name == "my_macro"); @@ -1013,17 +1032,17 @@ mod tests { InstructionMacroDefinition { name: "my_macro".into(), parameters: vec![], - contents: vec![AbstractOp::new(Caller)], + contents: vec![AbstractOp::new(HardForkOp::Cancun(Caller.into()))], } .into(), InstructionMacroDefinition { name: "my_macro".into(), parameters: vec![], - contents: vec![AbstractOp::new(Caller)], + contents: vec![AbstractOp::new(HardForkOp::Cancun(Caller.into()))], } .into(), ]; - let mut asm = Assembler::new(); + let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); let err = asm.assemble(ops).unwrap_err(); assert_matches!(err, Error::DuplicateMacro { name, .. } if name == "my_macro"); @@ -1043,7 +1062,7 @@ mod tests { "my_macro".into(), )), ]; - let mut asm = Assembler::new(); + let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); let err = asm.assemble(ops).unwrap_err(); assert_matches!(err, Error::DuplicateLabel { label, .. } if label == "a"); @@ -1055,22 +1074,22 @@ mod tests { fn assemble_conflicting_labels_in_instruction_macro() -> Result<(), Error> { let ops = vec![ AbstractOp::Label("a".into()), - AbstractOp::new(Caller), + AbstractOp::new(HardForkOp::Cancun(Caller.into())), InstructionMacroDefinition { name: "my_macro()".into(), parameters: vec![], contents: vec![ AbstractOp::Label("a".into()), - AbstractOp::new(Push1(Imm::with_label("a"))), + AbstractOp::new(HardForkOp::Cancun(Push1(Imm::with_label("a")).into())), ], } .into(), AbstractOp::Macro(InstructionMacroInvocation::with_zero_parameters( "my_macro()".into(), )), - AbstractOp::new(Push1(Imm::with_label("a"))), + AbstractOp::new(HardForkOp::Cancun(Push1(Imm::with_label("a")).into())), ]; - let mut asm = Assembler::new(); + let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); let result = asm.assemble(ops)?; assert_eq!(result, hex!("3360016000")); @@ -1085,14 +1104,14 @@ mod tests { name: "my_macro".into(), parameters: vec!["foo".into(), "bar".into()], contents: vec![ - AbstractOp::new(Push1(Imm::with_variable("foo"))), - AbstractOp::new(Push1(Imm::with_variable("bar"))), + AbstractOp::new(HardForkOp::Cancun(Push1(Imm::with_variable("foo")).into())), + AbstractOp::new(HardForkOp::Cancun(Push1(Imm::with_variable("bar")).into())), ], } .into(), AbstractOp::Label("b".into()), - AbstractOp::new(JumpDest), - AbstractOp::new(Push1(Imm::with_label("b"))), + AbstractOp::new(HardForkOp::Cancun(JumpDest.into())), + AbstractOp::new(HardForkOp::Cancun(Push1(Imm::with_label("b")).into())), AbstractOp::Macro(InstructionMacroInvocation { name: "my_macro".into(), parameters: vec![ @@ -1102,7 +1121,7 @@ mod tests { }), ]; - let mut asm = Assembler::new(); + let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); let result = asm.assemble(ops)?; assert_eq!(result, hex!("5b600060426000")); @@ -1111,11 +1130,11 @@ mod tests { #[test] fn assemble_expression_push() -> Result<(), Error> { - let ops = vec![AbstractOp::new(Push1(Imm::with_expression( - Expression::Plus(1.into(), 1.into()), - )))]; + let ops = vec![AbstractOp::new(HardForkOp::Cancun( + Push1(Imm::with_expression(Expression::Plus(1.into(), 1.into()))).into(), + ))]; - let mut asm = Assembler::new(); + let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); let result = asm.assemble(ops)?; assert_eq!(result, hex!("6002")); @@ -1124,10 +1143,10 @@ mod tests { #[test] fn assemble_expression_negative() -> Result<(), Error> { - let ops = vec![AbstractOp::new(Push1(Imm::with_expression( - BigInt::from(-1).into(), - )))]; - let mut asm = Assembler::new(); + let ops = vec![AbstractOp::new(HardForkOp::Cancun( + Push1(Imm::with_expression(BigInt::from(-1).into())).into(), + ))]; + let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); let err = asm.assemble(ops).unwrap_err(); assert_matches!(err, Error::ExpressionNegative { value, .. } if value == BigInt::from(-1)); @@ -1136,11 +1155,14 @@ mod tests { #[test] fn assemble_expression_undeclared_label() -> Result<(), Error> { - let mut asm = Assembler::new(); + let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); let err = asm - .assemble(vec![AbstractOp::new(Push1(Imm::with_expression( - Terminal::Label(String::from("hi")).into(), - )))]) + .assemble(vec![AbstractOp::new(HardForkOp::Cancun( + Push1(Imm::with_expression( + Terminal::Label(String::from("hi")).into(), + )) + .into(), + ))]) .unwrap_err(); assert_matches!(err, Error::UndeclaredLabels { labels, .. } if labels == vec!["hi"]); Ok(()) @@ -1148,15 +1170,15 @@ mod tests { #[test] fn assemble_variable_push_expression_with_undeclared_labels() -> Result<(), Error> { - let mut asm = Assembler::new(); + let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); let err = asm .assemble(vec![ - AbstractOp::new(JumpDest), + AbstractOp::new(HardForkOp::Cancun(JumpDest.into())), AbstractOp::Push(Imm::with_expression(Expression::Plus( Terminal::Label("foo".into()).into(), Terminal::Label("bar".into()).into(), ))), - AbstractOp::new(Gas), + AbstractOp::new(HardForkOp::Cancun(Gas.into())), ]) .unwrap_err(); // The expressions have short-circuit evaluation, so only the first label is caught in the error. @@ -1166,9 +1188,9 @@ mod tests { #[test] fn assemble_variable_push1_expression() -> Result<(), Error> { - let mut asm = Assembler::new(); + let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); let result = asm.assemble(vec![ - AbstractOp::new(JumpDest), + AbstractOp::new(HardForkOp::Cancun(JumpDest.into())), AbstractOp::Label("auto".into()), AbstractOp::Push(Imm::with_expression(Expression::Plus( 1.into(), @@ -1181,14 +1203,14 @@ mod tests { #[test] fn assemble_expression_with_labels() -> Result<(), Error> { - let mut asm = Assembler::new(); + let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); let result = asm.assemble(vec![ - AbstractOp::new(JumpDest), + AbstractOp::new(HardForkOp::Cancun(JumpDest.into())), AbstractOp::Push(Imm::with_expression(Expression::Plus( Terminal::Label(String::from("foo")).into(), Terminal::Label(String::from("bar")).into(), ))), - AbstractOp::new(Gas), + AbstractOp::new(HardForkOp::Cancun(Gas.into())), AbstractOp::Label("foo".into()), AbstractOp::Label("bar".into()), ])?; @@ -1205,13 +1227,16 @@ mod tests { content: Imm::with_expression(Expression::Plus(1.into(), 1.into())), } .into(), - AbstractOp::new(Push1(Imm::with_macro(ExpressionMacroInvocation { - name: "foo".into(), - parameters: vec![], - }))), + AbstractOp::new(HardForkOp::Cancun( + Push1(Imm::with_macro(ExpressionMacroInvocation { + name: "foo".into(), + parameters: vec![], + })) + .into(), + )), ]; - let mut asm = Assembler::new(); + let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); let result = asm.assemble(ops)?; assert_eq!(result, hex!("6002")); @@ -1224,19 +1249,21 @@ mod tests { InstructionMacroDefinition { name: "my_macro".into(), parameters: vec!["foo".into()], - contents: vec![AbstractOp::new(Push1(Imm::with_variable("bar")))], + contents: vec![AbstractOp::new(HardForkOp::Cancun( + Push1(Imm::with_variable("bar")).into(), + ))], } .into(), AbstractOp::Label("b".into()), - AbstractOp::new(JumpDest), - AbstractOp::new(Push1(Imm::with_label("b"))), + AbstractOp::new(HardForkOp::Cancun(JumpDest.into())), + AbstractOp::new(HardForkOp::Cancun(Push1(Imm::with_label("b")).into())), AbstractOp::Macro(InstructionMacroInvocation { name: "my_macro".into(), parameters: vec![BigInt::from_bytes_be(Sign::Plus, &vec![0x42]).into()], }), ]; - let mut asm = Assembler::new(); + let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); let err = asm.assemble(ops).unwrap_err(); assert_matches!(err, Error::UndeclaredVariableMacro { var, .. } if var == "bar"); @@ -1245,7 +1272,7 @@ mod tests { #[test] fn assemble_instruction_macro_two_delayed_definitions_mirrored() -> Result<(), Error> { let ops = vec![ - AbstractOp::new(GetPc), + AbstractOp::new(HardForkOp::Cancun(GetPc.into())), AbstractOp::Macro(InstructionMacroInvocation { name: "macro1".into(), parameters: vec![], @@ -1257,18 +1284,18 @@ mod tests { InstructionMacroDefinition { name: "macro0".into(), parameters: vec![], - contents: vec![AbstractOp::new(JumpDest)], + contents: vec![AbstractOp::new(HardForkOp::Cancun(JumpDest.into()))], } .into(), InstructionMacroDefinition { name: "macro1".into(), parameters: vec![], - contents: vec![AbstractOp::new(Caller)], + contents: vec![AbstractOp::new(HardForkOp::Cancun(Caller.into()))], } .into(), ]; - let mut asm = Assembler::new(); + let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); let result = asm.assemble(ops)?; assert_eq!(result, hex!("58335b")); @@ -1278,7 +1305,7 @@ mod tests { #[test] fn assemble_instruction_macro_two_delayed_definitions() -> Result<(), Error> { let ops = vec![ - AbstractOp::new(GetPc), + AbstractOp::new(HardForkOp::Cancun(GetPc.into())), AbstractOp::Macro(InstructionMacroInvocation { name: "macro0".into(), parameters: vec![], @@ -1290,18 +1317,18 @@ mod tests { InstructionMacroDefinition { name: "macro0".into(), parameters: vec![], - contents: vec![AbstractOp::new(JumpDest)], + contents: vec![AbstractOp::new(HardForkOp::Cancun(JumpDest.into()))], } .into(), InstructionMacroDefinition { name: "macro1".into(), parameters: vec![], - contents: vec![AbstractOp::new(Caller)], + contents: vec![AbstractOp::new(HardForkOp::Cancun(Caller.into()))], } .into(), ]; - let mut asm = Assembler::new(); + let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); let result = asm.assemble(ops)?; assert_eq!(result, hex!("585b33")); diff --git a/etk-asm/src/ast.rs b/etk-asm/src/ast.rs index e3824c15..bde8c943 100644 --- a/etk-asm/src/ast.rs +++ b/etk-asm/src/ast.rs @@ -1,7 +1,10 @@ use std::path::PathBuf; use crate::ops::{Abstract, AbstractOp, ExpressionMacroDefinition, InstructionMacroDefinition}; -use etk_ops::cancun::Op; +use etk_ops::cancun::Op as CancunOp; +use etk_ops::london::Op as LondonOp; +use etk_ops::prague::Op as PragueOp; +use etk_ops::shanghai::Op as ShanghaiOp; #[derive(Debug, Clone, PartialEq)] pub(crate) enum Node { @@ -10,9 +13,27 @@ pub(crate) enum Node { Include(PathBuf), IncludeHex(PathBuf), } -impl From> for Node { - fn from(op: Op) -> Self { - Node::Op(AbstractOp::Op(op)) +impl From> for Node { + fn from(op: CancunOp) -> Self { + Node::Op(AbstractOp::Op(etk_ops::HardForkOp::Cancun(op))) + } +} + +impl From> for Node { + fn from(op: ShanghaiOp) -> Self { + Node::Op(AbstractOp::Op(etk_ops::HardForkOp::Shanghai(op))) + } +} + +impl From> for Node { + fn from(op: PragueOp) -> Self { + Node::Op(AbstractOp::Op(etk_ops::HardForkOp::Prague(op))) + } +} + +impl From> for Node { + fn from(op: LondonOp) -> Self { + Node::Op(AbstractOp::Op(etk_ops::HardForkOp::London(op))) } } diff --git a/etk-asm/src/ops.rs b/etk-asm/src/ops.rs index 967008fe..618eb81f 100644 --- a/etk-asm/src/ops.rs +++ b/etk-asm/src/ops.rs @@ -43,7 +43,11 @@ mod types; pub(crate) use self::error::Error; -use etk_ops::cancun::Push32; +use etk_ops::cancun::Push32 as CancunPush32; +use etk_ops::london::Push32 as LondonPush32; +use etk_ops::prague::Push32 as PraguePush32; +use etk_ops::shanghai::Push32 as ShanghaiPush32; +use etk_ops::HardFork; use etk_ops::HardForkOp; use etk_ops::Operation; @@ -233,7 +237,11 @@ impl AbstractOp { Self::Op(op.into()) } - pub(crate) fn concretize(self, ctx: Context) -> Result, error::Error> { + pub(crate) fn concretize( + self, + ctx: Context, + hardfork: HardFork, + ) -> Result, error::Error> { match self { Self::Op(op) => op.concretize(ctx), Self::Push(imm) => { @@ -250,21 +258,50 @@ impl AbstractOp { ); if bytes.len() > 32 { + let push32 = match hardfork { + HardFork::Cancun => { + HardForkOp::Cancun(etk_ops::cancun::Op::Push32(CancunPush32(()))) + } + HardFork::Shanghai => { + HardForkOp::Shanghai(etk_ops::shanghai::Op::Push32(ShanghaiPush32(()))) + } + HardFork::London => { + HardForkOp::London(etk_ops::london::Op::Push32(LondonPush32(()))) + } + HardFork::Prague => { + HardForkOp::Prague(etk_ops::prague::Op::Push32(PraguePush32(()))) + } + }; // TODO: Fix hack to get a TryFromSliceError. let err = <[u8; 32]>::try_from(bytes.as_slice()) .context(error::ExpressionTooLarge { value, - spec: Push32(()), + spec: push32, }) .unwrap_err(); return Err(err); } let size = std::cmp::max(1, (value.bits() + 8 - 1) / 8); - let spec = Op::<()>::push(size.try_into().unwrap()).unwrap(); + //let spec = Op::<()>::push(size.try_into().unwrap()).unwrap(); + + let spec = match hardfork { + HardFork::Cancun => HardForkOp::Cancun( + etk_ops::cancun::Op::<()>::push(size.try_into().unwrap()).unwrap(), + ), + HardFork::Shanghai => HardForkOp::Shanghai( + etk_ops::shanghai::Op::<()>::push(size.try_into().unwrap()).unwrap(), + ), + HardFork::London => HardForkOp::London( + etk_ops::london::Op::<()>::push(size.try_into().unwrap()).unwrap(), + ), + HardFork::Prague => HardForkOp::Prague( + etk_ops::prague::Op::<()>::push(size.try_into().unwrap()).unwrap(), + ), + }; let start = bytes.len() + 1 - spec.size(); - AbstractOp::new(spec.with(&bytes[start..]).unwrap()).concretize(ctx) + AbstractOp::new(spec.with(&bytes[start..]).unwrap()).concretize(ctx, hardfork) } Self::Label(_) => panic!("labels cannot be concretized"), Self::Macro(_) => panic!("macros cannot be concretized"), diff --git a/etk-asm/src/parse/mod.rs b/etk-asm/src/parse/mod.rs index 4858abaa..43fef482 100644 --- a/etk-asm/src/parse/mod.rs +++ b/etk-asm/src/parse/mod.rs @@ -22,7 +22,6 @@ use self::{ use crate::ast::Node; use crate::ops::AbstractOp; -use etk_ops::cancun::Op; use num_bigint::BigInt; use pest::{iterators::Pair, Parser}; diff --git a/etk-ops/build.rs b/etk-ops/build.rs index 3976e42a..2dc85a89 100644 --- a/etk-ops/build.rs +++ b/etk-ops/build.rs @@ -366,6 +366,7 @@ fn generate_fork(fork_name: &str) -> Result<(), Error> { #[educe( PartialEq(bound = #partial_eq_bound), Eq(bound = #eq_bound), + //Clone(bound = #clone_bound), Ord(bound = #ord_bound), PartialOrd(bound = #partial_ord_bound), Hash(bound = #hash_bound), @@ -383,12 +384,11 @@ fn generate_fork(fork_name: &str) -> Result<(), Error> { } // TODO: For some reason deriving Debug with educe didn't work. - use std::fmt; - impl fmt::Debug for Op + impl std::fmt::Debug for Op where T: super::Immediates + ?Sized, { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "Op variant") } } @@ -397,11 +397,12 @@ fn generate_fork(fork_name: &str) -> Result<(), Error> { impl Clone for Op where T: super::Immediates + ?Sized, + #(T::#bounds: Clone,)* { fn clone(&self) -> Self { match self { #( - Self::#names(n) => Self::#names(n), + Self::#names(n) => Self::#names(n.clone()), )* } } diff --git a/etk-ops/src/lib.rs b/etk-ops/src/lib.rs index 50b602e9..f7f193e6 100644 --- a/etk-ops/src/lib.rs +++ b/etk-ops/src/lib.rs @@ -95,7 +95,7 @@ pub mod prague { } /// An operation that can be executed by the EVM. -#[derive(Debug, Clone)] +#[derive(Debug)] pub enum HardForkOp where T: Immediates + ?Sized, @@ -113,6 +113,137 @@ where Prague(prague::Op), } +impl Clone for HardForkOp +where + T: Immediates + ?Sized, + T::P1: Clone, + T::P2: Clone, + T::P3: Clone, + T::P4: Clone, + T::P5: Clone, + T::P6: Clone, + T::P7: Clone, + T::P8: Clone, + T::P9: Clone, + T::P10: Clone, + T::P11: Clone, + T::P12: Clone, + T::P13: Clone, + T::P14: Clone, + T::P15: Clone, + T::P16: Clone, + T::P17: Clone, + T::P18: Clone, + T::P19: Clone, + T::P20: Clone, + T::P21: Clone, + T::P22: Clone, + T::P23: Clone, + T::P24: Clone, + T::P25: Clone, + T::P26: Clone, + T::P27: Clone, + T::P28: Clone, + T::P29: Clone, + T::P30: Clone, + T::P31: Clone, + T::P32: Clone, +{ + fn clone(&self) -> Self { + match *self { + Self::Cancun(ref op) => Self::Cancun(op.clone()), + Self::Shanghai(ref op) => Self::Shanghai(op.clone()), + Self::London(ref op) => Self::London(op.clone()), + Self::Prague(ref op) => Self::Prague(op.clone()), + } + } +} + +impl PartialEq> for HardForkOp +where + T: Immediates + ?Sized, + T::P1: PartialEq, + T::P2: PartialEq, + T::P3: PartialEq, + T::P4: PartialEq, + T::P5: PartialEq, + T::P6: PartialEq, + T::P7: PartialEq, + T::P8: PartialEq, + T::P9: PartialEq, + T::P10: PartialEq, + T::P11: PartialEq, + T::P12: PartialEq, + T::P13: PartialEq, + T::P14: PartialEq, + T::P15: PartialEq, + T::P16: PartialEq, + T::P17: PartialEq, + T::P18: PartialEq, + T::P19: PartialEq, + T::P20: PartialEq, + T::P21: PartialEq, + T::P22: PartialEq, + T::P23: PartialEq, + T::P24: PartialEq, + T::P25: PartialEq, + T::P26: PartialEq, + T::P27: PartialEq, + T::P28: PartialEq, + T::P29: PartialEq, + T::P30: PartialEq, + T::P31: PartialEq, + T::P32: PartialEq, +{ + fn eq(&self, other: &HardForkOp) -> bool { + match (self, other) { + (Self::Cancun(op1), HardForkOp::Cancun(op2)) => op1 == op2, + (Self::Shanghai(op1), HardForkOp::Shanghai(op2)) => op1 == op2, + (Self::London(op1), HardForkOp::London(op2)) => op1 == op2, + (Self::Prague(op1), HardForkOp::Prague(op2)) => op1 == op2, + _ => false, + } + } +} + +impl Eq for HardForkOp +where + T: Immediates + ?Sized, + T::P1: Eq, + T::P2: Eq, + T::P3: Eq, + T::P4: Eq, + T::P5: Eq, + T::P6: Eq, + T::P7: Eq, + T::P8: Eq, + T::P9: Eq, + T::P10: Eq, + T::P11: Eq, + T::P12: Eq, + T::P13: Eq, + T::P14: Eq, + T::P15: Eq, + T::P16: Eq, + T::P17: Eq, + T::P18: Eq, + T::P19: Eq, + T::P20: Eq, + T::P21: Eq, + T::P22: Eq, + T::P23: Eq, + T::P24: Eq, + T::P25: Eq, + T::P26: Eq, + T::P27: Eq, + T::P28: Eq, + T::P29: Eq, + T::P30: Eq, + T::P31: Eq, + T::P32: Eq, +{ +} + impl HardForkOp where T: Immediates + ?Sized, @@ -126,6 +257,65 @@ where HardForkOp::Prague(op) => prague::Op::new(op).map(HardForkOp::Prague), } } + + /// Returns the total length of this operation, including its immediate. + pub fn size(&self) -> usize { + match self { + HardForkOp::Cancun(op) => op.size(), + HardForkOp::Shanghai(op) => op.size(), + HardForkOp::London(op) => op.size(), + HardForkOp::Prague(op) => op.size(), + } + } +} + +impl HardForkOp<()> { + /// Join this opcode with an immediate argument. + /// + /// Panics if this opcode does not take an immediate argument. + pub fn with(self, immediate: I) -> Result, E> + where + T: ?Sized + Immediates, + I: TryInto, + I: TryInto, + I: TryInto, + I: TryInto, + I: TryInto, + I: TryInto, + I: TryInto, + I: TryInto, + I: TryInto, + I: TryInto, + I: TryInto, + I: TryInto, + I: TryInto, + I: TryInto, + I: TryInto, + I: TryInto, + I: TryInto, + I: TryInto, + I: TryInto, + I: TryInto, + I: TryInto, + I: TryInto, + I: TryInto, + I: TryInto, + I: TryInto, + I: TryInto, + I: TryInto, + I: TryInto, + I: TryInto, + I: TryInto, + I: TryInto, + I: TryInto, + { + match self { + HardForkOp::Cancun(op) => Ok(HardForkOp::Cancun(op.with(immediate)?)), + HardForkOp::Shanghai(op) => Ok(HardForkOp::Shanghai(op.with(immediate)?)), + HardForkOp::London(op) => Ok(HardForkOp::London(op.with(immediate)?)), + HardForkOp::Prague(op) => Ok(HardForkOp::Prague(op.with(immediate)?)), + } + } } impl Operation for HardForkOp @@ -236,6 +426,17 @@ impl From> for u8 { } } +impl std::fmt::Display for HardForkOp<()> { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + HardForkOp::Cancun(op) => op.fmt(f), + HardForkOp::Shanghai(op) => op.fmt(f), + HardForkOp::London(op) => op.fmt(f), + HardForkOp::Prague(op) => op.fmt(f), + } + } +} + /// Hard forks of the Ethereum Virtual Machine. #[derive(Debug, Clone)] pub enum HardFork { From 25c28bb657f3e346a7886735f9db1134f22559f0 Mon Sep 17 00:00:00 2001 From: Gaston Zanitti Date: Thu, 19 Oct 2023 16:56:25 -0300 Subject: [PATCH 35/73] More progress towards Hardfork selection --- etk-asm/src/asm.rs | 7 +- etk-asm/src/ingest.rs | 2 +- etk-asm/src/parse/macros.rs | 12 ++- etk-asm/src/parse/mod.rs | 131 ++++++++++++++++++++----------- etk-dasm/src/blocks/annotated.rs | 1 + etk-dasm/src/blocks/basic.rs | 3 +- 6 files changed, 102 insertions(+), 54 deletions(-) diff --git a/etk-asm/src/asm.rs b/etk-asm/src/asm.rs index 0fbbb87f..b2c3334e 100644 --- a/etk-asm/src/asm.rs +++ b/etk-asm/src/asm.rs @@ -230,11 +230,12 @@ struct PendingLabel { } impl Assembler { - /// Create a new `Assembler`. + /// Create a new `Assembler` for the last hardfork. pub fn new() -> Self { Self::default() } + /// Create a new `Assembler` for an specific hardfork. pub fn new_with_hardfork(hardfork: HardFork) -> Self { Self { hardfork, @@ -277,7 +278,7 @@ impl Assembler { if let RawOp::Op(ref op) = op { match op.clone().concretize( (&self.declared_labels, &self.declared_macros).into(), - self.hardfork, + self.hardfork.clone(), ) { Ok(cop) => cop.assemble(&mut output), Err(ops::Error::ContextIncomplete { @@ -436,7 +437,7 @@ impl Assembler { RawOp::Op(ref op) => { match op.clone().concretize( (&self.declared_labels, &self.declared_macros).into(), - self.hardfork, + self.hardfork.clone(), ) { Ok(cop) => { self.concrete_len += cop.size(); diff --git a/etk-asm/src/ingest.rs b/etk-asm/src/ingest.rs index c90d5077..52a7a4c2 100644 --- a/etk-asm/src/ingest.rs +++ b/etk-asm/src/ingest.rs @@ -309,7 +309,7 @@ where } fn preprocess(&mut self, program: &mut Program, src: &str) -> Result, Error> { - let nodes = parse_asm(src).context(error::Parse { + let nodes = parse_asm(src, self.hardfork.clone()).context(error::Parse { path: program.sources.last().unwrap().clone(), })?; let mut raws = Vec::new(); diff --git a/etk-asm/src/parse/macros.rs b/etk-asm/src/parse/macros.rs index c262917f..2e157ec1 100644 --- a/etk-asm/src/parse/macros.rs +++ b/etk-asm/src/parse/macros.rs @@ -7,15 +7,16 @@ use crate::ops::{ AbstractOp, Expression, ExpressionMacroDefinition, ExpressionMacroInvocation, InstructionMacroDefinition, InstructionMacroInvocation, }; +use etk_ops::HardFork; use pest::iterators::Pair; use std::path::PathBuf; -pub(crate) fn parse(pair: Pair) -> Result { +pub(crate) fn parse(pair: Pair, hardfork: HardFork) -> Result { let mut pairs = pair.into_inner(); let pair = pairs.next().unwrap(); match pair.as_rule() { - Rule::instruction_macro_definition => parse_instruction_macro_defn(pair), + Rule::instruction_macro_definition => parse_instruction_macro_defn(pair, hardfork), Rule::instruction_macro => parse_instruction_macro(pair), Rule::expression_macro_definition => parse_expression_macro_defn(pair), _ => unreachable!(), @@ -51,7 +52,10 @@ pub(crate) fn parse_builtin(pair: Pair) -> Result { Ok(node) } -fn parse_instruction_macro_defn(pair: Pair) -> Result { +fn parse_instruction_macro_defn( + pair: Pair, + hardfork: HardFork, +) -> Result { let mut pairs = pair.into_inner(); let mut macro_defn = pairs.next().unwrap().into_inner(); @@ -68,7 +72,7 @@ fn parse_instruction_macro_defn(pair: Pair) -> Result Result, ParseError> { +pub(crate) fn parse_asm(asm: &str, hardfork: HardFork) -> Result, ParseError> { let mut program: Vec = Vec::new(); let pairs = AsmParser::parse(Rule::program, asm)?; @@ -33,7 +39,7 @@ pub(crate) fn parse_asm(asm: &str) -> Result, ParseError> { let node = match pair.as_rule() { Rule::builtin => macros::parse_builtin(pair)?, Rule::EOI => continue, - _ => parse_abstract_op(pair)?.into(), + _ => parse_abstract_op(pair, hardfork.clone())?.into(), }; program.push(node); } @@ -41,16 +47,29 @@ pub(crate) fn parse_asm(asm: &str) -> Result, ParseError> { Ok(program) } -fn parse_abstract_op(pair: Pair) -> Result { +fn parse_abstract_op(pair: Pair, hardfork: HardFork) -> Result { let ret = match pair.as_rule() { - Rule::local_macro => macros::parse(pair)?, + Rule::local_macro => macros::parse(pair, hardfork)?, Rule::label_definition => { AbstractOp::Label(pair.into_inner().next().unwrap().as_str().to_string()) } - Rule::push => parse_push(pair)?, + Rule::push => parse_push(pair, hardfork)?, Rule::op => { - let spec: Op<()> = pair.as_str().parse().unwrap(); - let op = Op::new(spec).unwrap(); + let op: HardForkOp<_> = match hardfork { + HardFork::Cancun => { + HardForkOp::Cancun(CancunOp::new(pair.as_str().parse().unwrap()).unwrap()) + } + HardFork::Shanghai => { + HardForkOp::Shanghai(ShanghaiOp::new(pair.as_str().parse().unwrap()).unwrap()) + } + HardFork::Prague => { + HardForkOp::Prague(PragueOp::new(pair.as_str().parse().unwrap()).unwrap()) + } + HardFork::London => { + HardForkOp::London(LondonOp::new(pair.as_str().parse().unwrap()).unwrap()) + } + }; + AbstractOp::Op(op) } _ => unreachable!(), @@ -59,13 +78,20 @@ fn parse_abstract_op(pair: Pair) -> Result { Ok(ret) } -fn parse_push(pair: Pair) -> Result { +fn parse_push(pair: Pair, hardfork: HardFork) -> Result { let mut pair = pair.into_inner(); let size = pair.next().unwrap(); let size: usize = size.as_str().parse().unwrap(); let operand = pair.next().unwrap(); - let spec = Op::<()>::push(size).unwrap(); + let spec = match hardfork { + HardFork::Cancun => HardForkOp::Cancun(CancunOp::<()>::push(size).unwrap()), + HardFork::Shanghai => HardForkOp::Shanghai(ShanghaiOp::<()>::push(size).unwrap()), + HardFork::Prague => HardForkOp::Prague(PragueOp::<()>::push(size).unwrap()), + HardFork::London => HardForkOp::London(LondonOp::<()>::push(size).unwrap()), + }; + + //let spec = Op::<()>::push(size).unwrap(); let expr = expression::parse(operand)?; if let Ok(val) = expr.eval() { @@ -113,7 +139,7 @@ mod tests { Op::from(Xor), Op::from(Push0) ]; - assert_matches!(parse_asm(asm), Ok(e) if e == expected); + assert_matches!(parse_asm(asm, HardFork::Cancun), Ok(e) if e == expected); } #[test] @@ -125,7 +151,7 @@ mod tests { Op::from(Push1(Imm::from([0]))), Op::from(Push1(Imm::from([1]))) ]; - assert_matches!(parse_asm(asm), Ok(e) if e == expected); + assert_matches!(parse_asm(asm, HardFork::Cancun), Ok(e) if e == expected); } #[test] @@ -139,7 +165,7 @@ mod tests { Op::from(Push1(Imm::from([1]))), Op::from(Push1(Imm::from([1]))) ]; - assert_matches!(parse_asm(asm), Ok(e) if e == expected); + assert_matches!(parse_asm(asm, HardFork::Cancun), Ok(e) if e == expected); } #[test] @@ -153,7 +179,7 @@ mod tests { Op::from(Push1(Imm::from([0]))), Op::from(Push1(Imm::from([1]))) ]; - assert_matches!(parse_asm(asm), Ok(e) if e == expected); + assert_matches!(parse_asm(asm, HardFork::Cancun), Ok(e) if e == expected); } #[test] @@ -169,8 +195,8 @@ mod tests { Op::from(Push1(Imm::from([7]))), Op::from(Push2(Imm::from([1, 0]))), ]; - println!("{:?}\n\n{:?}", parse_asm(asm), expected); - assert_matches!(parse_asm(asm), Ok(e) if e == expected); + println!("{:?}\n\n{:?}", parse_asm(asm, HardFork::Cancun), expected); + assert_matches!(parse_asm(asm, HardFork::Cancun), Ok(e) if e == expected); } #[test] @@ -196,10 +222,13 @@ mod tests { Op::from(Push2(Imm::from(hex!("0100")))), Op::from(Push4(Imm::from(hex!("ffffffff")))), ]; - assert_matches!(parse_asm(asm), Ok(e) if e == expected); + assert_matches!(parse_asm(asm, HardFork::Cancun), Ok(e) if e == expected); let asm = "push1 256"; - assert_matches!(parse_asm(asm), Err(ParseError::ImmediateTooLarge { .. })); + assert_matches!( + parse_asm(asm, HardFork::Cancun), + Err(ParseError::ImmediateTooLarge { .. }) + ); } #[test] @@ -228,10 +257,13 @@ mod tests { "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20" )))), ]; - assert_matches!(parse_asm(asm), Ok(e) if e == expected); + assert_matches!(parse_asm(asm, HardFork::Cancun), Ok(e) if e == expected); let asm = "push2 0x010203"; - assert_matches!(parse_asm(asm), Err(ParseError::ImmediateTooLarge { .. })); + assert_matches!( + parse_asm(asm, HardFork::Cancun), + Err(ParseError::ImmediateTooLarge { .. }) + ); } #[test] @@ -256,21 +288,21 @@ mod tests { Op::from(Log0), Op::from(Log4), ]; - assert_matches!(parse_asm(asm), Ok(e) if e == expected); + assert_matches!(parse_asm(asm, HardFork::Cancun), Ok(e) if e == expected); } #[test] fn parse_jumpdest_no_label() { let asm = "jumpdest"; let expected = nodes![Op::from(JumpDest)]; - assert_matches!(parse_asm(asm), Ok(e) if e == expected); + assert_matches!(parse_asm(asm, HardFork::Cancun), Ok(e) if e == expected); } #[test] fn parse_jumpdest_label() { let asm = "start:\njumpdest"; let expected = nodes![AbstractOp::Label("start".into()), Op::from(JumpDest),]; - assert_matches!(parse_asm(asm), Ok(e) if e == expected); + assert_matches!(parse_asm(asm, HardFork::Cancun), Ok(e) if e == expected); } #[test] @@ -283,7 +315,7 @@ mod tests { Op::from(Push2(Imm::with_label("snake_case"))), Op::from(JumpI) ]; - assert_matches!(parse_asm(asm), Ok(e) if e == expected); + assert_matches!(parse_asm(asm, HardFork::Cancun), Ok(e) if e == expected); } #[test] @@ -298,7 +330,7 @@ mod tests { Op::from(Push1(Imm::with_label("push1"))), Op::from(JumpI), ]; - assert_matches!(parse_asm(asm), Ok(e) if e == expected); + assert_matches!(parse_asm(asm, HardFork::Cancun), Ok(e) if e == expected); } #[test] @@ -319,8 +351,8 @@ mod tests { "a9059cbb2ab09eb219583f4a59a5d0623ade346d962bcd4e46b11da047c9049b" )))), ]; - println!("{:?}\n\n{:?}", parse_asm(asm), expected); - assert_matches!(parse_asm(asm), Ok(e) if e == expected); + println!("{:?}\n\n{:?}", parse_asm(asm, HardFork::Cancun), expected); + assert_matches!(parse_asm(asm, HardFork::Cancun), Ok(e) if e == expected); } #[test] @@ -328,7 +360,10 @@ mod tests { let asm = r#" push4 selector("name( )") "#; - assert_matches!(parse_asm(asm), Err(ParseError::Lexer { .. })); + assert_matches!( + parse_asm(asm, HardFork::Cancun), + Err(ParseError::Lexer { .. }) + ); } #[test] @@ -345,7 +380,7 @@ mod tests { Node::Include(PathBuf::from("foo.asm")), Op::from(Push1(Imm::from(2u8))), ]; - assert_matches!(parse_asm(&asm), Ok(e) if e == expected) + assert_matches!(parse_asm(&asm, HardFork::Cancun), Ok(e) if e == expected) } #[test] @@ -362,7 +397,7 @@ mod tests { Node::IncludeHex(PathBuf::from("foo.hex")), Op::from(Push1(Imm::from(2u8))), ]; - assert_matches!(parse_asm(&asm), Ok(e) if e == expected) + assert_matches!(parse_asm(&asm, HardFork::Cancun), Ok(e) if e == expected) } #[test] @@ -379,7 +414,7 @@ mod tests { Node::Import(PathBuf::from("foo.asm")), Op::from(Push1(Imm::from(2u8))), ]; - assert_matches!(parse_asm(&asm), Ok(e) if e == expected) + assert_matches!(parse_asm(&asm, HardFork::Cancun), Ok(e) if e == expected) } #[test] @@ -390,7 +425,7 @@ mod tests { "#, ); assert!(matches!( - parse_asm(&asm), + parse_asm(&asm, HardFork::Cancun), Err(ParseError::ExtraArgument { expected: 1, backtrace: _ @@ -406,7 +441,7 @@ mod tests { "#, ); assert!(matches!( - parse_asm(&asm), + parse_asm(&asm, HardFork::Cancun), Err(ParseError::MissingArgument { got: 0, expected: 1, @@ -422,7 +457,10 @@ mod tests { %import(0x44) "#, ); - assert_matches!(parse_asm(&asm), Err(ParseError::ArgumentType { .. })) + assert_matches!( + parse_asm(&asm, HardFork::Cancun), + Err(ParseError::ArgumentType { .. }) + ) } #[test] @@ -439,7 +477,7 @@ mod tests { Node::Import(PathBuf::from("hello.asm")), Op::from(Push1(Imm::from(2u8))), ]; - assert_matches!(parse_asm(&asm), Ok(e) if e == expected) + assert_matches!(parse_asm(&asm, HardFork::Cancun), Ok(e) if e == expected) } #[test] @@ -456,7 +494,7 @@ mod tests { AbstractOp::Push(Imm::with_label("hello")), Op::from(Push1(Imm::from(2u8))), ]; - assert_matches!(parse_asm(&asm), Ok(e) if e == expected) + assert_matches!(parse_asm(&asm, HardFork::Cancun), Ok(e) if e == expected) } #[test] @@ -479,12 +517,15 @@ mod tests { name: "my_macro".into(), parameters: vec!["foo".into(), "bar".into()], contents: vec![ - AbstractOp::new(GasPrice), - AbstractOp::new(Pop), - AbstractOp::new(Push1( - Expression::Plus( - Terminal::Variable("foo".to_string()).into(), - Terminal::Variable("bar".to_string()).into() + AbstractOp::new(HardForkOp::Cancun(GasPrice.into())), + AbstractOp::new(HardForkOp::Cancun(Pop.into())), + AbstractOp::new(HardForkOp::Cancun( + Push1( + Expression::Plus( + Terminal::Variable("foo".to_string()).into(), + Terminal::Variable("bar".to_string()).into() + ) + .into() ) .into() )), @@ -510,7 +551,7 @@ mod tests { }) ]; - assert_eq!(parse_asm(&asm).unwrap(), expected) + assert_eq!(parse_asm(&asm, HardFork::Cancun).unwrap(), expected) } #[test] @@ -553,7 +594,7 @@ mod tests { 2.into() )))) ]; - assert_eq!(parse_asm(&asm).unwrap(), expected) + assert_eq!(parse_asm(&asm, HardFork::Cancun).unwrap(), expected) } #[test] @@ -570,7 +611,7 @@ mod tests { AbstractOp::Push(Imm::with_expression(Expression::Plus(1.into(), 1.into()))), Op::from(Push1(Imm::from(2u8))), ]; - assert_matches!(parse_asm(&asm), Ok(e) if e == expected) + assert_matches!(parse_asm(&asm, HardFork::Cancun), Ok(e) if e == expected) } #[test] @@ -594,6 +635,6 @@ mod tests { parameters: vec![] }))), ]; - assert_eq!(parse_asm(&asm).unwrap(), expected); + assert_eq!(parse_asm(&asm, HardFork::Cancun).unwrap(), expected); } } diff --git a/etk-dasm/src/blocks/annotated.rs b/etk-dasm/src/blocks/annotated.rs index 382c17e3..cacb16e7 100644 --- a/etk-dasm/src/blocks/annotated.rs +++ b/etk-dasm/src/blocks/annotated.rs @@ -3,6 +3,7 @@ use crate::sym::{Expr, Var}; use etk_ops::cancun::*; +use etk_ops::Operation; use std::collections::VecDeque; diff --git a/etk-dasm/src/blocks/basic.rs b/etk-dasm/src/blocks/basic.rs index ee0b2abf..70463806 100644 --- a/etk-dasm/src/blocks/basic.rs +++ b/etk-dasm/src/blocks/basic.rs @@ -1,7 +1,8 @@ //! A list of EVM instructions with a single point of entry and a single exit. use etk_asm::disasm::Offset; -use etk_ops::cancun::{Op, Operation}; +use etk_ops::cancun::Op; +use etk_ops::Operation; /// A list of EVM instructions with a single point of entry and a single exit. #[derive(Debug, Eq, PartialEq)] From 7f7d593ce1c9f2ecb91f19e2ac0eeb95b194c98c Mon Sep 17 00:00:00 2001 From: Gaston Zanitti Date: Fri, 20 Oct 2023 16:30:28 -0300 Subject: [PATCH 36/73] Fixed import --- etk-asm/src/asm.rs | 2 +- etk-asm/src/bin/eas.rs | 2 +- etk-dasm/src/bin/disease/selectors.rs | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/etk-asm/src/asm.rs b/etk-asm/src/asm.rs index b2c3334e..63bcb158 100644 --- a/etk-asm/src/asm.rs +++ b/etk-asm/src/asm.rs @@ -6,7 +6,7 @@ mod error { use crate::ops::Expression; use crate::ParseError; - use etk_ops::{cancun::Op, HardForkOp}; + use etk_ops::HardForkOp; use num_bigint::BigInt; use snafu::{Backtrace, Snafu}; diff --git a/etk-asm/src/bin/eas.rs b/etk-asm/src/bin/eas.rs index a5ab83f8..0c7fdbc2 100644 --- a/etk-asm/src/bin/eas.rs +++ b/etk-asm/src/bin/eas.rs @@ -53,7 +53,7 @@ fn run() -> Result<(), Error> { None => HardFork::default(), }; - println!("Hardfork: {:?}.", hardfork); + println!("Hardfork selected: {:?}.", hardfork); let hex_out = HexWrite::new(&mut out); diff --git a/etk-dasm/src/bin/disease/selectors.rs b/etk-dasm/src/bin/disease/selectors.rs index ad21a6c9..280eb5de 100644 --- a/etk-dasm/src/bin/disease/selectors.rs +++ b/etk-dasm/src/bin/disease/selectors.rs @@ -1,6 +1,7 @@ use etk_4byte::reverse_selector; -use etk_ops::cancun::{Op, Operation}; +use etk_ops::cancun::Op; +use etk_ops::Operation; use std::fmt; From 25e69b6247348f64da70015164e97f08a9a76f11 Mon Sep 17 00:00:00 2001 From: Gaston Zanitti Date: Fri, 20 Oct 2023 17:06:38 -0300 Subject: [PATCH 37/73] etk_ops added to etk_analyze --- Cargo.lock | 1 + etk-analyze/Cargo.toml | 12 ++++++++++-- etk-analyze/src/cfg.rs | 3 ++- etk-asm/src/bin/eas.rs | 3 +-- 4 files changed, 14 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d2a9264e..c642e060 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -422,6 +422,7 @@ dependencies = [ "etk-asm", "etk-cli", "etk-dasm", + "etk-ops", "hex-literal", "petgraph", "snafu", diff --git a/etk-analyze/Cargo.toml b/etk-analyze/Cargo.toml index 886c1f38..34504701 100644 --- a/etk-analyze/Cargo.toml +++ b/etk-analyze/Cargo.toml @@ -2,14 +2,21 @@ name = "etk-analyze" version = "0.4.0-dev" edition = "2018" -authors = ["Sam Wilson ", "lightclient "] +authors = [ + "Sam Wilson ", + "lightclient ", +] license = "MIT OR Apache-2.0" description = "EVM Toolkit analysis tools" homepage = "https://quilt.github.io/etk" repository = "https://github.com/quilt/etk" readme = "README.md" keywords = ["etk", "ethereum"] -categories = ["cryptography::cryptocurrencies", "command-line-utilities", "development-tools"] +categories = [ + "cryptography::cryptocurrencies", + "command-line-utilities", + "development-tools", +] [features] cli = ["etk-cli", "etk-asm", "clap", "snafu"] @@ -20,6 +27,7 @@ clap = { optional = true, version = "3.1", features = ["derive"] } etk-cli = { optional = true, path = "../etk-cli", version = "0.4.0-dev" } etk-asm = { optional = true, path = "../etk-asm", version = "0.4.0-dev" } etk-dasm = { path = "../etk-dasm", version = "0.4.0-dev" } +etk-ops = { path = "../etk-ops", version = "0.4.0-dev" } z3 = { version = "0.11.2", features = ["static-link-z3"] } [dependencies.petgraph] diff --git a/etk-analyze/src/cfg.rs b/etk-analyze/src/cfg.rs index d52cbb87..ab7c1f8f 100644 --- a/etk-analyze/src/cfg.rs +++ b/etk-analyze/src/cfg.rs @@ -1,6 +1,7 @@ use crate::blocks::annotated::ExitExt; use etk_dasm::blocks::annotated::{AnnotatedBlock, Exit}; +use etk_ops::HardFork; use petgraph::dot::Dot; use petgraph::graph::{Graph, NodeIndex}; @@ -328,7 +329,7 @@ mod tests { { fn compile(&self) -> Disassembler { let mut output = Disassembler::new(); - Ingest::new(&mut output) + Ingest::new(&mut output, HardFork::Cancun) .ingest("./test", self.source) .unwrap(); output diff --git a/etk-asm/src/bin/eas.rs b/etk-asm/src/bin/eas.rs index 0c7fdbc2..b9415012 100644 --- a/etk-asm/src/bin/eas.rs +++ b/etk-asm/src/bin/eas.rs @@ -47,8 +47,7 @@ fn run() -> Result<(), Error> { None => Box::new(std::io::stdout()), }; - let opthard = opt.hardfork.clone(); - let mut hardfork = match opt.hardfork { + let hardfork = match opt.hardfork { Some(h) => h, None => HardFork::default(), }; From 0acdf8cb98aa0e3f561b680c0e5c2fa3296cb58d Mon Sep 17 00:00:00 2001 From: Gaston Zanitti Date: Fri, 20 Oct 2023 17:15:52 -0300 Subject: [PATCH 38/73] etk_ops added to etk_analyze --- etk-analyze/src/cfg.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etk-analyze/src/cfg.rs b/etk-analyze/src/cfg.rs index ab7c1f8f..82bf9f39 100644 --- a/etk-analyze/src/cfg.rs +++ b/etk-analyze/src/cfg.rs @@ -1,7 +1,6 @@ use crate::blocks::annotated::ExitExt; use etk_dasm::blocks::annotated::{AnnotatedBlock, Exit}; -use etk_ops::HardFork; use petgraph::dot::Dot; use petgraph::graph::{Graph, NodeIndex}; @@ -296,6 +295,7 @@ mod tests { use etk_asm::disasm::Disassembler; use etk_asm::ingest::Ingest; + use etk_ops::HardFork; use etk_dasm::blocks::basic::Separator; From 57e8df58eb4a9035cd1d88daedee1c88f86442d2 Mon Sep 17 00:00:00 2001 From: Gaston Zanitti Date: Fri, 20 Oct 2023 17:35:20 -0300 Subject: [PATCH 39/73] Test moved from Default (Prague) to Cancun due to jumpdest deprecation --- etk-asm/src/ingest.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/etk-asm/src/ingest.rs b/etk-asm/src/ingest.rs index 52a7a4c2..15bb4049 100644 --- a/etk-asm/src/ingest.rs +++ b/etk-asm/src/ingest.rs @@ -244,7 +244,7 @@ impl Program { /// "#; /// /// let mut output = Vec::new(); -/// let mut ingest = Ingest::new(&mut output, HardFork::default()); +/// let mut ingest = Ingest::new(&mut output, HardFork::Cancun); /// ingest.ingest("./example.etk", &text)?; /// /// # let expected = hex!("6100035b"); @@ -406,7 +406,7 @@ mod tests { ); let mut output = Vec::new(); - let mut ingest = Ingest::new(&mut output, HardFork::default()); + let mut ingest = Ingest::new(&mut output, HardFork::Cancun); ingest.ingest(root, &text)?; assert_eq!(output, hex!("6001602a6002")); @@ -435,7 +435,7 @@ mod tests { ); let mut output = Vec::new(); - let mut ingest = Ingest::new(&mut output, HardFork::default()); + let mut ingest = Ingest::new(&mut output, HardFork::Cancun); ingest.ingest(root, &text)?; assert_eq!(output, hex!("60015b586000566002")); @@ -464,7 +464,7 @@ mod tests { ); let mut output = Vec::new(); - let mut ingest = Ingest::new(&mut output, HardFork::default()); + let mut ingest = Ingest::new(&mut output, HardFork::Cancun); let err = ingest.ingest(root, &text).unwrap_err(); assert_matches!( @@ -489,7 +489,7 @@ mod tests { ); let mut output = Vec::new(); - let mut ingest = Ingest::new(&mut output, HardFork::default()); + let mut ingest = Ingest::new(&mut output, HardFork::Cancun); ingest.ingest(root, &text)?; assert_eq!(output, hex!("6001deadbeef0102f66002")); @@ -513,7 +513,7 @@ mod tests { ); let mut output = Vec::new(); - let mut ingest = Ingest::new(&mut output, HardFork::default()); + let mut ingest = Ingest::new(&mut output, HardFork::Cancun); ingest.ingest(root, &text)?; assert_eq!(output, hex!("6001deadbeef0102f65b600960ff")); @@ -535,7 +535,7 @@ mod tests { ); let mut output = Vec::new(); - let mut ingest = Ingest::new(&mut output, HardFork::default()); + let mut ingest = Ingest::new(&mut output, HardFork::Cancun); ingest.ingest(root, &text)?; let expected = hex!("61001caaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa5b"); @@ -578,7 +578,7 @@ mod tests { ); let mut output = Vec::new(); - let mut ingest = Ingest::new(&mut output, HardFork::default()); + let mut ingest = Ingest::new(&mut output, HardFork::Cancun); ingest.ingest(root, &text)?; let expected = hex!("620000096200000e5b5b6008600e5b610008610009"); @@ -627,7 +627,7 @@ mod tests { ); let mut output = Vec::new(); - let mut ingest = Ingest::new(&mut output, HardFork::default()); + let mut ingest = Ingest::new(&mut output, HardFork::Cancun); ingest.ingest(root, &text)?; let expected = hex!("620000155b58600b5b5b61000b6100035b600360045b62000004"); @@ -648,7 +648,7 @@ mod tests { ); let mut output = Vec::new(); - let mut ingest = Ingest::new(&mut output, HardFork::default()); + let mut ingest = Ingest::new(&mut output, HardFork::Cancun); let root = std::env::current_exe().unwrap(); let err = ingest.ingest(root, &text).unwrap_err(); @@ -669,7 +669,7 @@ mod tests { ); let mut output = Vec::new(); - let mut ingest = Ingest::new(&mut output, HardFork::default()); + let mut ingest = Ingest::new(&mut output, HardFork::Cancun); let err = ingest.ingest(root, &text).unwrap_err(); assert_matches!(err, Error::RecursionLimit { .. }); From 0743a792046558cfc3ae215654eac48640bc6a0f Mon Sep 17 00:00:00 2001 From: Gaston Zanitti Date: Fri, 20 Oct 2023 18:06:14 -0300 Subject: [PATCH 40/73] Hardfork Prague removed until needed --- etk-asm/src/ast.rs | 7 - etk-asm/src/ops.rs | 17 - etk-asm/src/parse/mod.rs | 6 - etk-asm/tests/asm.rs | 20 +- etk-ops/build.rs | 1 - etk-ops/src/lib.rs | 32 +- etk-ops/src/prague.toml | 921 --------------------------------------- 7 files changed, 11 insertions(+), 993 deletions(-) delete mode 100644 etk-ops/src/prague.toml diff --git a/etk-asm/src/ast.rs b/etk-asm/src/ast.rs index bde8c943..dd348f4f 100644 --- a/etk-asm/src/ast.rs +++ b/etk-asm/src/ast.rs @@ -3,7 +3,6 @@ use std::path::PathBuf; use crate::ops::{Abstract, AbstractOp, ExpressionMacroDefinition, InstructionMacroDefinition}; use etk_ops::cancun::Op as CancunOp; use etk_ops::london::Op as LondonOp; -use etk_ops::prague::Op as PragueOp; use etk_ops::shanghai::Op as ShanghaiOp; #[derive(Debug, Clone, PartialEq)] @@ -25,12 +24,6 @@ impl From> for Node { } } -impl From> for Node { - fn from(op: PragueOp) -> Self { - Node::Op(AbstractOp::Op(etk_ops::HardForkOp::Prague(op))) - } -} - impl From> for Node { fn from(op: LondonOp) -> Self { Node::Op(AbstractOp::Op(etk_ops::HardForkOp::London(op))) diff --git a/etk-asm/src/ops.rs b/etk-asm/src/ops.rs index 618eb81f..45fe7ce1 100644 --- a/etk-asm/src/ops.rs +++ b/etk-asm/src/ops.rs @@ -45,7 +45,6 @@ pub(crate) use self::error::Error; use etk_ops::cancun::Push32 as CancunPush32; use etk_ops::london::Push32 as LondonPush32; -use etk_ops::prague::Push32 as PraguePush32; use etk_ops::shanghai::Push32 as ShanghaiPush32; use etk_ops::HardFork; use etk_ops::HardForkOp; @@ -147,16 +146,6 @@ impl Concretize for HardForkOp { })?; HardForkOp::London(op_context) } - HardForkOp::Prague(op) => { - let op_context = - op.code() - .with(bytes.as_slice()) - .context(error::ExpressionTooLarge { - value, - spec: self.code(), - })?; - HardForkOp::Prague(op_context) - } }; Ok(result) @@ -268,9 +257,6 @@ impl AbstractOp { HardFork::London => { HardForkOp::London(etk_ops::london::Op::Push32(LondonPush32(()))) } - HardFork::Prague => { - HardForkOp::Prague(etk_ops::prague::Op::Push32(PraguePush32(()))) - } }; // TODO: Fix hack to get a TryFromSliceError. let err = <[u8; 32]>::try_from(bytes.as_slice()) @@ -295,9 +281,6 @@ impl AbstractOp { HardFork::London => HardForkOp::London( etk_ops::london::Op::<()>::push(size.try_into().unwrap()).unwrap(), ), - HardFork::Prague => HardForkOp::Prague( - etk_ops::prague::Op::<()>::push(size.try_into().unwrap()).unwrap(), - ), }; let start = bytes.len() + 1 - spec.size(); diff --git a/etk-asm/src/parse/mod.rs b/etk-asm/src/parse/mod.rs index 1d42bcd4..016c2283 100644 --- a/etk-asm/src/parse/mod.rs +++ b/etk-asm/src/parse/mod.rs @@ -22,7 +22,6 @@ use self::{ use etk_ops::cancun::Op as CancunOp; use etk_ops::london::Op as LondonOp; -use etk_ops::prague::Op as PragueOp; use etk_ops::shanghai::Op as ShanghaiOp; use crate::ast::Node; @@ -62,9 +61,6 @@ fn parse_abstract_op(pair: Pair, hardfork: HardFork) -> Result { HardForkOp::Shanghai(ShanghaiOp::new(pair.as_str().parse().unwrap()).unwrap()) } - HardFork::Prague => { - HardForkOp::Prague(PragueOp::new(pair.as_str().parse().unwrap()).unwrap()) - } HardFork::London => { HardForkOp::London(LondonOp::new(pair.as_str().parse().unwrap()).unwrap()) } @@ -87,11 +83,9 @@ fn parse_push(pair: Pair, hardfork: HardFork) -> Result HardForkOp::Cancun(CancunOp::<()>::push(size).unwrap()), HardFork::Shanghai => HardForkOp::Shanghai(ShanghaiOp::<()>::push(size).unwrap()), - HardFork::Prague => HardForkOp::Prague(PragueOp::<()>::push(size).unwrap()), HardFork::London => HardForkOp::London(LondonOp::<()>::push(size).unwrap()), }; - //let spec = Op::<()>::push(size).unwrap(); let expr = expression::parse(operand)?; if let Ok(val) = expr.eval() { diff --git a/etk-asm/tests/asm.rs b/etk-asm/tests/asm.rs index 251e4007..86e62212 100644 --- a/etk-asm/tests/asm.rs +++ b/etk-asm/tests/asm.rs @@ -21,7 +21,7 @@ where #[test] fn simple_constructor() -> Result<(), Error> { let mut output = Vec::new(); - let mut ingester = Ingest::new(&mut output, HardFork::default()); + let mut ingester = Ingest::new(&mut output, HardFork::Cancun); ingester.ingest_file(source(&["simple-constructor", "ctor.etk"]))?; assert_eq!( @@ -40,7 +40,7 @@ fn simple_constructor() -> Result<(), Error> { #[test] fn out_of_bounds() { let mut output = Vec::new(); - let mut ingester = Ingest::new(&mut output, HardFork::default()); + let mut ingester = Ingest::new(&mut output, HardFork::Cancun); let err = ingester .ingest_file(source(&["out-of-bounds", "main", "main.etk"])) .unwrap_err(); @@ -51,7 +51,7 @@ fn out_of_bounds() { #[test] fn subdirectory() -> Result<(), Error> { let mut output = Vec::new(); - let mut ingester = Ingest::new(&mut output, HardFork::default()); + let mut ingester = Ingest::new(&mut output, HardFork::Cancun); ingester.ingest_file(source(&["subdirectory", "main.etk"]))?; assert_eq!(output, hex!("63c001c0de60ff")); @@ -62,7 +62,7 @@ fn subdirectory() -> Result<(), Error> { #[test] fn variable_jump() -> Result<(), Error> { let mut output = Vec::new(); - let mut ingester = Ingest::new(&mut output, HardFork::default()); + let mut ingester = Ingest::new(&mut output, HardFork::Cancun); ingester.ingest_file(source(&["variable-jump", "main.etk"]))?; assert_eq!(output, hex!("6003565b")); @@ -73,7 +73,7 @@ fn variable_jump() -> Result<(), Error> { #[test] fn instruction_macro() -> Result<(), Error> { let mut output = Vec::new(); - let mut ingester = Ingest::new(&mut output, HardFork::default()); + let mut ingester = Ingest::new(&mut output, HardFork::Cancun); ingester.ingest_file(source(&["instruction-macro", "main.etk"]))?; assert_eq!( @@ -87,7 +87,7 @@ fn instruction_macro() -> Result<(), Error> { #[test] fn instruction_macro_with_empty_lines() -> Result<(), Error> { let mut output = Vec::new(); - let mut ingester = Ingest::new(&mut output, HardFork::default()); + let mut ingester = Ingest::new(&mut output, HardFork::Cancun); ingester.ingest_file(source(&["instruction-macro", "empty_lines.etk"]))?; assert_eq!(output, hex!("6000600060006000600060006000")); @@ -98,7 +98,7 @@ fn instruction_macro_with_empty_lines() -> Result<(), Error> { #[test] fn instruction_macro_with_two_instructions_per_line() { let mut output = Vec::new(); - let mut ingester = Ingest::new(&mut output, HardFork::default()); + let mut ingester = Ingest::new(&mut output, HardFork::Cancun); let err = ingester .ingest_file(source(&[ "instruction-macro", @@ -112,7 +112,7 @@ fn instruction_macro_with_two_instructions_per_line() { #[test] fn undefined_label_undefined_macro() { let mut output = Vec::new(); - let mut ingester = Ingest::new(&mut output, HardFork::default()); + let mut ingester = Ingest::new(&mut output, HardFork::Cancun); let err = ingester .ingest_file(source(&[ "instruction-macro", @@ -128,7 +128,7 @@ fn undefined_label_undefined_macro() { #[test] fn every_op() -> Result<(), Error> { let mut output = Vec::new(); - let mut ingester = Ingest::new(&mut output, HardFork::default()); + let mut ingester = Ingest::new(&mut output, HardFork::Cancun); ingester.ingest_file(source(&["every-op", "main.etk"]))?; assert_eq!( @@ -297,7 +297,7 @@ fn every_op() -> Result<(), Error> { #[test] fn test_dynamic_push_and_include() -> Result<(), Error> { let mut output = Vec::new(); - let mut ingester = Ingest::new(&mut output, HardFork::default()); + let mut ingester = Ingest::new(&mut output, HardFork::Cancun); ingester.ingest_file(source(&["variable-push", "main.etk"]))?; assert_eq!(output, hex!("61025758585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585801010101010158585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858015b58")); diff --git a/etk-ops/build.rs b/etk-ops/build.rs index 2dc85a89..fdadc92b 100644 --- a/etk-ops/build.rs +++ b/etk-ops/build.rs @@ -819,5 +819,4 @@ fn main() { generate_fork("london").unwrap(); generate_fork("shanghai").unwrap(); generate_fork("cancun").unwrap(); - generate_fork("prague").unwrap(); } diff --git a/etk-ops/src/lib.rs b/etk-ops/src/lib.rs index f7f193e6..f6c8c595 100644 --- a/etk-ops/src/lib.rs +++ b/etk-ops/src/lib.rs @@ -89,11 +89,6 @@ pub mod cancun { include!(concat!(env!("OUT_DIR"), "/cancun.rs")); } -pub mod prague { - //! Instructions available in the Prague hard fork. - include!(concat!(env!("OUT_DIR"), "/prague.rs")); -} - /// An operation that can be executed by the EVM. #[derive(Debug)] pub enum HardForkOp @@ -108,9 +103,6 @@ where /// London hard fork operations. London(london::Op), - - /// Prague hard fork operations. - Prague(prague::Op), } impl Clone for HardForkOp @@ -154,7 +146,6 @@ where Self::Cancun(ref op) => Self::Cancun(op.clone()), Self::Shanghai(ref op) => Self::Shanghai(op.clone()), Self::London(ref op) => Self::London(op.clone()), - Self::Prague(ref op) => Self::Prague(op.clone()), } } } @@ -200,7 +191,6 @@ where (Self::Cancun(op1), HardForkOp::Cancun(op2)) => op1 == op2, (Self::Shanghai(op1), HardForkOp::Shanghai(op2)) => op1 == op2, (Self::London(op1), HardForkOp::London(op2)) => op1 == op2, - (Self::Prague(op1), HardForkOp::Prague(op2)) => op1 == op2, _ => false, } } @@ -254,7 +244,6 @@ where HardForkOp::Cancun(op) => cancun::Op::new(op).map(HardForkOp::Cancun), HardForkOp::Shanghai(op) => shanghai::Op::new(op).map(HardForkOp::Shanghai), HardForkOp::London(op) => london::Op::new(op).map(HardForkOp::London), - HardForkOp::Prague(op) => prague::Op::new(op).map(HardForkOp::Prague), } } @@ -264,7 +253,6 @@ where HardForkOp::Cancun(op) => op.size(), HardForkOp::Shanghai(op) => op.size(), HardForkOp::London(op) => op.size(), - HardForkOp::Prague(op) => op.size(), } } } @@ -313,7 +301,6 @@ impl HardForkOp<()> { HardForkOp::Cancun(op) => Ok(HardForkOp::Cancun(op.with(immediate)?)), HardForkOp::Shanghai(op) => Ok(HardForkOp::Shanghai(op.with(immediate)?)), HardForkOp::London(op) => Ok(HardForkOp::London(op.with(immediate)?)), - HardForkOp::Prague(op) => Ok(HardForkOp::Prague(op.with(immediate)?)), } } } @@ -330,7 +317,6 @@ where HardForkOp::Cancun(op) => op.immediate(), HardForkOp::Shanghai(op) => op.immediate(), HardForkOp::London(op) => op.immediate(), - HardForkOp::Prague(op) => op.immediate(), } } fn immediate_mut(&mut self) -> Option<&mut Self::ImmediateRef> { @@ -338,7 +324,6 @@ where HardForkOp::Cancun(op) => op.immediate_mut(), HardForkOp::Shanghai(op) => op.immediate_mut(), HardForkOp::London(op) => op.immediate_mut(), - HardForkOp::Prague(op) => op.immediate_mut(), } } fn into_immediate(self) -> Option { @@ -346,7 +331,6 @@ where HardForkOp::Cancun(op) => op.into_immediate(), HardForkOp::Shanghai(op) => op.into_immediate(), HardForkOp::London(op) => op.into_immediate(), - HardForkOp::Prague(op) => op.into_immediate(), } } fn extra_len(&self) -> usize { @@ -354,7 +338,6 @@ where HardForkOp::Cancun(op) => op.extra_len(), HardForkOp::Shanghai(op) => op.extra_len(), HardForkOp::London(op) => op.extra_len(), - HardForkOp::Prague(op) => op.extra_len(), } } fn code(&self) -> Self::Code { @@ -362,7 +345,6 @@ where HardForkOp::Cancun(op) => HardForkOp::Cancun(op.code()), HardForkOp::Shanghai(op) => HardForkOp::Shanghai(op.code()), HardForkOp::London(op) => HardForkOp::London(op.code()), - HardForkOp::Prague(op) => HardForkOp::Prague(op.code()), } } fn mnemonic(&self) -> &str { @@ -370,7 +352,6 @@ where HardForkOp::Cancun(op) => op.mnemonic(), HardForkOp::Shanghai(op) => op.mnemonic(), HardForkOp::London(op) => op.mnemonic(), - HardForkOp::Prague(op) => op.mnemonic(), } } fn is_jump(&self) -> bool { @@ -378,7 +359,6 @@ where HardForkOp::Cancun(op) => op.is_jump(), HardForkOp::Shanghai(op) => op.is_jump(), HardForkOp::London(op) => op.is_jump(), - HardForkOp::Prague(op) => op.is_jump(), } } fn is_jump_target(&self) -> bool { @@ -386,7 +366,6 @@ where HardForkOp::Cancun(op) => op.is_jump_target(), HardForkOp::Shanghai(op) => op.is_jump_target(), HardForkOp::London(op) => op.is_jump_target(), - HardForkOp::Prague(op) => op.is_jump_target(), } } fn is_exit(&self) -> bool { @@ -394,7 +373,6 @@ where HardForkOp::Cancun(op) => op.is_exit(), HardForkOp::Shanghai(op) => op.is_exit(), HardForkOp::London(op) => op.is_exit(), - HardForkOp::Prague(op) => op.is_exit(), } } fn pops(&self) -> usize { @@ -402,7 +380,6 @@ where HardForkOp::Cancun(op) => op.pops(), HardForkOp::Shanghai(op) => op.pops(), HardForkOp::London(op) => op.pops(), - HardForkOp::Prague(op) => op.pops(), } } fn pushes(&self) -> usize { @@ -410,7 +387,6 @@ where HardForkOp::Cancun(op) => op.pushes(), HardForkOp::Shanghai(op) => op.pushes(), HardForkOp::London(op) => op.pushes(), - HardForkOp::Prague(op) => op.pushes(), } } } @@ -421,7 +397,6 @@ impl From> for u8 { HardForkOp::Cancun(op) => op.code_byte(), HardForkOp::Shanghai(op) => op.code_byte(), HardForkOp::London(op) => op.code_byte(), - HardForkOp::Prague(op) => op.code_byte(), } } } @@ -432,7 +407,6 @@ impl std::fmt::Display for HardForkOp<()> { HardForkOp::Cancun(op) => op.fmt(f), HardForkOp::Shanghai(op) => op.fmt(f), HardForkOp::London(op) => op.fmt(f), - HardForkOp::Prague(op) => op.fmt(f), } } } @@ -448,14 +422,11 @@ pub enum HardFork { /// The London hard fork. London, - - /// The Prague hard fork. - Prague, } impl Default for HardFork { fn default() -> Self { - Self::Prague + Self::Cancun } } @@ -466,7 +437,6 @@ impl From<&OsStr> for HardFork { "cancun" => Self::Cancun, "shanghai" => Self::Shanghai, "london" => Self::London, - "prague" => Self::Prague, _ => Self::default(), } } diff --git a/etk-ops/src/prague.toml b/etk-ops/src/prague.toml deleted file mode 100644 index 4d95eeac..00000000 --- a/etk-ops/src/prague.toml +++ /dev/null @@ -1,921 +0,0 @@ -[Stop] -code = 0x00 -mnemonic = "stop" -pushes = 0 -pops = 0 -exits = true - -[Add] -code = 0x01 -mnemonic = "add" -pushes = 1 -pops = 2 - -[Mul] -code = 0x02 -mnemonic = "mul" -pushes = 1 -pops = 2 - -[Sub] -code = 0x03 -mnemonic = "sub" -pushes = 1 -pops = 2 - -[Div] -code = 0x04 -mnemonic = "div" -pushes = 1 -pops = 2 - -[SDiv] -code = 0x05 -mnemonic = "sdiv" -pushes = 1 -pops = 2 - -[Mod] -code = 0x06 -mnemonic = "mod" -pushes = 1 -pops = 2 - -[SMod] -code = 0x07 -mnemonic = "smod" -pushes = 1 -pops = 2 - -[AddMod] -code = 0x08 -mnemonic = "addmod" -pushes = 1 -pops = 3 - -[MulMod] -code = 0x09 -mnemonic = "mulmod" -pushes = 1 -pops = 3 - -[Exp] -code = 0x0a -mnemonic = "exp" -pushes = 1 -pops = 2 - -[SignExtend] -code = 0x0b -mnemonic = "signextend" -pushes = 1 -pops = 2 - -[Lt] -code = 0x10 -mnemonic = "lt" -pushes = 1 -pops = 2 - -[Gt] -code = 0x11 -mnemonic = "gt" -pushes = 1 -pops = 2 - -[SLt] -code = 0x12 -mnemonic = "slt" -pushes = 1 -pops = 2 - -[SGt] -code = 0x13 -mnemonic = "sgt" -pushes = 1 -pops = 2 - -[Eq] -code = 0x14 -mnemonic = "eq" -pushes = 1 -pops = 2 - -[IsZero] -code = 0x15 -mnemonic = "iszero" -pushes = 1 -pops = 1 - -[And] -code = 0x16 -mnemonic = "and" -pushes = 1 -pops = 2 - -[Or] -code = 0x17 -mnemonic = "or" -pushes = 1 -pops = 2 - -[Xor] -code = 0x18 -mnemonic = "xor" -pushes = 1 -pops = 2 - -[Not] -code = 0x19 -mnemonic = "not" -pushes = 1 -pops = 1 - -[Byte] -code = 0x1a -mnemonic = "byte" -pushes = 1 -pops = 2 - -[Shl] -code = 0x1b -mnemonic = "shl" -pushes = 1 -pops = 2 - -[Shr] -code = 0x1c -mnemonic = "shr" -pushes = 1 -pops = 2 - -[Sar] -code = 0x1d -mnemonic = "sar" -pushes = 1 -pops = 2 - -[Keccak256] -code = 0x20 -mnemonic = "keccak256" -pushes = 1 -pops = 2 - -[Address] -code = 0x30 -mnemonic = "address" -pushes = 1 -pops = 0 - -[Balance] -code = 0x31 -mnemonic = "balance" -pushes = 1 -pops = 1 - -[Origin] -code = 0x32 -mnemonic = "origin" -pushes = 1 -pops = 0 - -[Caller] -code = 0x33 -mnemonic = "caller" -pushes = 1 -pops = 0 - -[CallValue] -code = 0x34 -mnemonic = "callvalue" -pushes = 1 -pops = 0 - -[CallDataLoad] -code = 0x35 -mnemonic = "calldataload" -pushes = 1 -pops = 1 - -[CallDataSize] -code = 0x36 -mnemonic = "calldatasize" -pushes = 1 -pops = 0 - -[CallDataCopy] -code = 0x37 -mnemonic = "calldatacopy" -pushes = 0 -pops = 3 - -[CodeSize] -code = 0x38 -mnemonic = "codesize" -pushes = 1 -pops = 0 - -[CodeCopy] -code = 0x39 -mnemonic = "codecopy" -pushes = 0 -pops = 3 - -[GasPrice] -code = 0x3a -mnemonic = "gasprice" -pushes = 1 -pops = 0 - -[ExtCodeSize] -code = 0x3b -mnemonic = "extcodesize" -pushes = 1 -pops = 1 - -[ExtCodeCopy] -code = 0x3c -mnemonic = "extcodecopy" -pushes = 0 -pops = 4 - -[ReturnDataSize] -code = 0x3d -mnemonic = "returndatasize" -pushes = 1 -pops = 0 - -[ReturnDataCopy] -code = 0x3e -mnemonic = "returndatacopy" -pushes = 0 -pops = 3 - -[ExtCodeHash] -code = 0x3f -mnemonic = "extcodehash" -pushes = 1 -pops = 1 - -[BlockHash] -code = 0x40 -mnemonic = "blockhash" -pushes = 1 -pops = 1 - -[Coinbase] -code = 0x41 -mnemonic = "coinbase" -pushes = 1 -pops = 0 - -[Timestamp] -code = 0x42 -mnemonic = "timestamp" -pushes = 1 -pops = 0 - -[Number] -code = 0x43 -mnemonic = "number" -pushes = 1 -pops = 0 - -[Difficulty] -code = 0x44 -mnemonic = "difficulty" -pushes = 1 -pops = 0 - -[GasLimit] -code = 0x45 -mnemonic = "gaslimit" -pushes = 1 -pops = 0 - -[ChainId] -code = 0x46 -mnemonic = "chainid" -pushes = 1 -pops = 0 - -[SelfBalance] -code = 0x47 -mnemonic = "selfbalance" -pushes = 1 -pops = 0 - -[BaseFee] -code = 0x48 -mnemonic = "basefee" -pushes = 1 -pops = 0 - -[Pop] -code = 0x50 -mnemonic = "pop" -pushes = 0 -pops = 1 - -[MLoad] -code = 0x51 -mnemonic = "mload" -pushes = 1 -pops = 1 - -[MStore] -code = 0x52 -mnemonic = "mstore" -pushes = 0 -pops = 2 - -[MStore8] -code = 0x53 -mnemonic = "mstore8" -pushes = 1 -pops = 2 - -[SLoad] -code = 0x54 -mnemonic = "sload" -pushes = 1 -pops = 1 - -[SStore] -code = 0x55 -mnemonic = "sstore" -pushes = 0 -pops = 2 - -[Jump] -code = 0x56 -mnemonic = "jump" -pushes = 0 -pops = 1 -jump = true - -[JumpI] -code = 0x57 -mnemonic = "jumpi" -pushes = 0 -pops = 2 -jump = true - -[GetPc] -code = 0x58 -mnemonic = "pc" -pushes = 1 -pops = 0 - -[MSize] -code = 0x59 -mnemonic = "msize" -pushes = 1 -pops = 0 - -[Gas] -code = 0x5a -mnemonic = "gas" -pushes = 1 -pops = 0 - -[Nop] -code = 0x5b -mnemonic = "nop" -pushes = 0 -pops = 0 - -[MCopy] -code = 0x5e -mnemonic = "mcopy" -pushes = 0 -pops = 3 - -[Push0] -code = 0x5f -mnemonic = "push0" -extra_len = 0 -pushes = 1 -pops = 0 - -[Push1] -code = 0x60 -mnemonic = "push1" -extra_len = 1 -pushes = 1 -pops = 0 - -[Push2] -code = 0x61 -mnemonic = "push2" -extra_len = 2 -pushes = 1 -pops = 0 - -[Push3] -code = 0x62 -mnemonic = "push3" -extra_len = 3 -pushes = 1 -pops = 0 - -[Push4] -code = 0x63 -mnemonic = "push4" -extra_len = 4 -pushes = 1 -pops = 0 - -[Push5] -code = 0x64 -mnemonic = "push5" -extra_len = 5 -pushes = 1 -pops = 0 - -[Push6] -code = 0x65 -mnemonic = "push6" -extra_len = 6 -pushes = 1 -pops = 0 - -[Push7] -code = 0x66 -mnemonic = "push7" -extra_len = 7 -pushes = 1 -pops = 0 - -[Push8] -code = 0x67 -mnemonic = "push8" -extra_len = 8 -pushes = 1 -pops = 0 - -[Push9] -code = 0x68 -mnemonic = "push9" -extra_len = 9 -pushes = 1 -pops = 0 - -[Push10] -code = 0x69 -mnemonic = "push10" -extra_len = 10 -pushes = 1 -pops = 0 - -[Push11] -code = 0x6a -mnemonic = "push11" -extra_len = 11 -pushes = 1 -pops = 0 - -[Push12] -code = 0x6b -mnemonic = "push12" -extra_len = 12 -pushes = 1 -pops = 0 - -[Push13] -code = 0x6c -mnemonic = "push13" -extra_len = 13 -pushes = 1 -pops = 0 - -[Push14] -code = 0x6d -mnemonic = "push14" -extra_len = 14 -pushes = 1 -pops = 0 - -[Push15] -code = 0x6e -mnemonic = "push15" -extra_len = 15 -pushes = 1 -pops = 0 - -[Push16] -code = 0x6f -mnemonic = "push16" -extra_len = 16 -pushes = 1 -pops = 0 - -[Push17] -code = 0x70 -mnemonic = "push17" -extra_len = 17 -pushes = 1 -pops = 0 - -[Push18] -code = 0x71 -mnemonic = "push18" -extra_len = 18 -pushes = 1 -pops = 0 - -[Push19] -code = 0x72 -mnemonic = "push19" -extra_len = 19 -pushes = 1 -pops = 0 - -[Push20] -code = 0x73 -mnemonic = "push20" -extra_len = 20 -pushes = 1 -pops = 0 - -[Push21] -code = 0x74 -mnemonic = "push21" -extra_len = 21 -pushes = 1 -pops = 0 - -[Push22] -code = 0x75 -mnemonic = "push22" -extra_len = 22 -pushes = 1 -pops = 0 - -[Push23] -code = 0x76 -mnemonic = "push23" -extra_len = 23 -pushes = 1 -pops = 0 - -[Push24] -code = 0x77 -mnemonic = "push24" -extra_len = 24 -pushes = 1 -pops = 0 - -[Push25] -code = 0x78 -mnemonic = "push25" -extra_len = 25 -pushes = 1 -pops = 0 - -[Push26] -code = 0x79 -mnemonic = "push26" -extra_len = 26 -pushes = 1 -pops = 0 - -[Push27] -code = 0x7a -mnemonic = "push27" -extra_len = 27 -pushes = 1 -pops = 0 - -[Push28] -code = 0x7b -mnemonic = "push28" -extra_len = 28 -pushes = 1 -pops = 0 - -[Push29] -code = 0x7c -mnemonic = "push29" -extra_len = 29 -pushes = 1 -pops = 0 - -[Push30] -code = 0x7d -mnemonic = "push30" -extra_len = 30 -pushes = 1 -pops = 0 - -[Push31] -code = 0x7e -mnemonic = "push31" -extra_len = 31 -pushes = 1 -pops = 0 - -[Push32] -code = 0x7f -mnemonic = "push32" -extra_len = 32 -pushes = 1 -pops = 0 - -[Dup1] -code = 0x80 -mnemonic = "dup1" -pushes = 2 -pops = 1 - -[Dup2] -code = 0x81 -mnemonic = "dup2" -pushes = 3 -pops = 2 - -[Dup3] -code = 0x82 -mnemonic = "dup3" -pushes = 4 -pops = 3 - -[Dup4] -code = 0x83 -mnemonic = "dup4" -pushes = 5 -pops = 4 - -[Dup5] -code = 0x84 -mnemonic = "dup5" -pushes = 6 -pops = 5 - -[Dup6] -code = 0x85 -mnemonic = "dup6" -pushes = 7 -pops = 6 - -[Dup7] -code = 0x86 -mnemonic = "dup7" -pushes = 8 -pops = 7 - -[Dup8] -code = 0x87 -mnemonic = "dup8" -pushes = 9 -pops = 8 - -[Dup9] -code = 0x88 -mnemonic = "dup9" -pushes = 10 -pops = 9 - -[Dup10] -code = 0x89 -mnemonic = "dup10" -pushes = 11 -pops = 10 - -[Dup11] -code = 0x8a -mnemonic = "dup11" -pushes = 12 -pops = 11 - -[Dup12] -code = 0x8b -mnemonic = "dup12" -pushes = 13 -pops = 12 - -[Dup13] -code = 0x8c -mnemonic = "dup13" -pushes = 14 -pops = 13 - -[Dup14] -code = 0x8d -mnemonic = "dup14" -pushes = 15 -pops = 14 - -[Dup15] -code = 0x8e -mnemonic = "dup15" -pushes = 16 -pops = 15 - -[Dup16] -code = 0x8f -mnemonic = "dup16" -pushes = 17 -pops = 16 - -[Swap1] -code = 0x90 -mnemonic = "swap1" -pushes = 2 -pops = 2 - -[Swap2] -code = 0x91 -mnemonic = "swap2" -pushes = 3 -pops = 3 - -[Swap3] -code = 0x92 -mnemonic = "swap3" -pushes = 4 -pops = 4 - -[Swap4] -code = 0x93 -mnemonic = "swap4" -pushes = 5 -pops = 5 - -[Swap5] -code = 0x94 -mnemonic = "swap5" -pushes = 6 -pops = 6 - -[Swap6] -code = 0x95 -mnemonic = "swap6" -pushes = 7 -pops = 7 - -[Swap7] -code = 0x96 -mnemonic = "swap7" -pushes = 8 -pops = 8 - -[Swap8] -code = 0x97 -mnemonic = "swap8" -pushes = 9 -pops = 9 - -[Swap9] -code = 0x98 -mnemonic = "swap9" -pushes = 10 -pops = 10 - -[Swap10] -code = 0x99 -mnemonic = "swap10" -pushes = 11 -pops = 11 - -[Swap11] -code = 0x9a -mnemonic = "swap11" -pushes = 12 -pops = 12 - -[Swap12] -code = 0x9b -mnemonic = "swap12" -pushes = 13 -pops = 13 - -[Swap13] -code = 0x9c -mnemonic = "swap13" -pushes = 14 -pops = 14 - -[Swap14] -code = 0x9d -mnemonic = "swap14" -pushes = 15 -pops = 15 - -[Swap15] -code = 0x9e -mnemonic = "swap15" -pushes = 16 -pops = 16 - -[Swap16] -code = 0x9f -mnemonic = "swap16" -pushes = 17 -pops = 17 - -[Log0] -code = 0xa0 -mnemonic = "log0" -pushes = 2 -pops = 2 - -[Log1] -code = 0xa1 -mnemonic = "log1" -pushes = 3 -pops = 3 - -[Log2] -code = 0xa2 -mnemonic = "log2" -pushes = 4 -pops = 4 - -[Log3] -code = 0xa3 -mnemonic = "log3" -pushes = 5 -pops = 5 - -[Log4] -code = 0xa4 -mnemonic = "log4" -pushes = 6 -pops = 6 - -[Callf] -code = 0xe3 -mnemonic = "callf" -extra_len = 16 -pushes = 0 -pops = 0 - -[Retf] -code = 0xe4 -mnemonic = "retf" -pushes = 0 -pops = 0 - -[Create] -code = 0xf0 -mnemonic = "create" -pushes = 1 -pops = 3 - -[Call] -code = 0xf1 -mnemonic = "call" -pushes = 1 -pops = 7 - -[CallCode] -code = 0xf2 -mnemonic = "callcode" -pushes = 1 -pops = 7 - -[Return] -code = 0xf3 -mnemonic = "return" -pushes = 0 -pops = 2 -exits = true - -[DelegateCall] -code = 0xf4 -mnemonic = "delegatecall" -pushes = 1 -pops = 6 - -[Create2] -code = 0xf5 -mnemonic = "create2" -pushes = 1 -pops = 4 - -[StaticCall] -code = 0xfa -mnemonic = "staticcall" -pushes = 1 -pops = 6 - -[Revert] -code = 0xfd -mnemonic = "revert" -pushes = 0 -pops = 2 -exits = true - -[Invalid] -code = 0xfe -mnemonic = "invalid" -pushes = 0 -pops = 0 -exits = true - -[SelfDestruct] -code = 0xff -mnemonic = "selfdestruct" -pushes = 0 -pops = 2 From 1771e88ade5dbf70140b301d907dd0a472c0b00a Mon Sep 17 00:00:00 2001 From: Gaston Zanitti Date: Fri, 20 Oct 2023 18:24:02 -0300 Subject: [PATCH 41/73] Better error reporting --- etk-asm/src/bin/eas.rs | 2 +- etk-ops/src/lib.rs | 23 ++++++++++++----------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/etk-asm/src/bin/eas.rs b/etk-asm/src/bin/eas.rs index b9415012..e0ceb9b5 100644 --- a/etk-asm/src/bin/eas.rs +++ b/etk-asm/src/bin/eas.rs @@ -18,7 +18,7 @@ struct Opt { #[structopt(parse(from_os_str))] out: Option, - #[structopt(long, parse(from_os_str))] + #[structopt(long)] hardfork: Option, } diff --git a/etk-ops/src/lib.rs b/etk-ops/src/lib.rs index f6c8c595..498b5adc 100644 --- a/etk-ops/src/lib.rs +++ b/etk-ops/src/lib.rs @@ -12,10 +12,7 @@ use snafu::{Backtrace, Snafu}; -use std::{ - borrow::{Borrow, BorrowMut}, - ffi::OsStr, -}; +use std::borrow::{Borrow, BorrowMut}; /// Trait for types that represent an EVM instruction. pub trait Operation { @@ -430,14 +427,18 @@ impl Default for HardFork { } } -impl From<&OsStr> for HardFork { - fn from(s: &OsStr) -> Self { - let s = s.to_string_lossy().to_lowercase(); +use std::str::FromStr; + +impl FromStr for HardFork { + type Err = String; + + fn from_str(s: &str) -> Result { + let s = s.to_lowercase(); match s.as_str() { - "cancun" => Self::Cancun, - "shanghai" => Self::Shanghai, - "london" => Self::London, - _ => Self::default(), + "cancun" => Ok(Self::Cancun), + "shanghai" => Ok(Self::Shanghai), + "london" => Ok(Self::London), + _ => Err(format!("Invalid hardfork: {}", s)), } } } From 202022963285ce375c735f437175bf34ac984f7b Mon Sep 17 00:00:00 2001 From: Gaston Zanitti Date: Fri, 20 Oct 2023 18:39:15 -0300 Subject: [PATCH 42/73] Various fixes --- etk-asm/src/asm.rs | 3 ++- etk-asm/src/ingest.rs | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/etk-asm/src/asm.rs b/etk-asm/src/asm.rs index 63bcb158..4ff95fb7 100644 --- a/etk-asm/src/asm.rs +++ b/etk-asm/src/asm.rs @@ -184,6 +184,7 @@ impl From> for RawOp { /// use etk_asm::asm::Assembler; /// use etk_asm::ops::AbstractOp; /// use etk_ops::cancun::{Op, GetPc}; +/// use etk_ops::{HardFork, HardForkOp}; /// # use etk_asm::asm::Error; /// # /// # use hex_literal::hex; @@ -577,7 +578,7 @@ mod tests { InstructionMacroDefinition, InstructionMacroInvocation, Terminal, }; use assert_matches::assert_matches; - use etk_ops::{cancun::*, HardForkOp}; + use etk_ops::{cancun::*, HardFork, HardForkOp}; use hex_literal::hex; use num_bigint::{BigInt, Sign}; diff --git a/etk-asm/src/ingest.rs b/etk-asm/src/ingest.rs index 15bb4049..00a85dd3 100644 --- a/etk-asm/src/ingest.rs +++ b/etk-asm/src/ingest.rs @@ -232,6 +232,7 @@ impl Program { /// /// ```rust /// use etk_asm::ingest::Ingest; +/// use etk_ops::HardFork; /// # /// # use etk_asm::ingest::Error; /// # From f2a32aba69f38664ee4fb66cdbd0ad44f71b49aa Mon Sep 17 00:00:00 2001 From: Gaston Zanitti Date: Mon, 23 Oct 2023 17:55:36 -0300 Subject: [PATCH 43/73] Updated doc --- doc/src/ch02-lang/README.md | 3 ++- doc/src/ch02-lang/ch02-expressions.md | 12 ++++++++---- doc/src/ch02-lang/ch03-labels.md | 6 ++++-- doc/src/ch02-lang/ch04-macros/README.md | 6 ++++-- doc/src/ch02-lang/ch04-macros/ch01-builtins.md | 9 ++++++--- doc/src/ch02-lang/ch04-macros/ch02-expression.md | 6 ++++-- doc/src/ch02-lang/ch04-macros/ch03-instruction.md | 6 ++++-- 7 files changed, 32 insertions(+), 16 deletions(-) diff --git a/doc/src/ch02-lang/README.md b/doc/src/ch02-lang/README.md index 6b35f108..9b9e7641 100644 --- a/doc/src/ch02-lang/README.md +++ b/doc/src/ch02-lang/README.md @@ -12,6 +12,7 @@ This example should increment a value from 0 to 255 on the stack, then halt exec ```rust # extern crate etk_asm; +# extern crate etk_ops; # let src = r#" push1 0x00 @@ -28,7 +29,7 @@ loop: pop stop # This halts execution # "#; -# let mut ingest = etk_asm::ingest::Ingest::new(Vec::new()); +# let mut ingest = etk_asm::ingest::Ingest::new(Vec::new(), etk_ops::HardFork::Cancun); # ingest.ingest(file!(), src).unwrap(); ``` diff --git a/doc/src/ch02-lang/ch02-expressions.md b/doc/src/ch02-lang/ch02-expressions.md index 456d506c..b66c4f88 100644 --- a/doc/src/ch02-lang/ch02-expressions.md +++ b/doc/src/ch02-lang/ch02-expressions.md @@ -10,11 +10,12 @@ While an assembled `push` must have a concrete value, it is often useful when de ```rust # extern crate etk_asm; +# extern crate etk_ops # let src = r#" push1 1+(2*3)/4 # "#; # let mut output = Vec::new(); -# let mut ingest = etk_asm::ingest::Ingest::new(&mut output); +# let mut ingest = etk_asm::ingest::Ingest::new(&mut output, etk_ops::HardFork::Cancun); # ingest.ingest(file!(), src).unwrap(); # assert_eq!(output, &[0x60, 0x02]); ``` @@ -50,12 +51,13 @@ A [label](ch03-labels.md) may be used as a term in an expression. ```rust # extern crate etk_asm; +# extern crate etk_ops; # let src = r#" start: push1 start + 1 # "#; # let mut output = Vec::new(); -# let mut ingest = etk_asm::ingest::Ingest::new(&mut output); +# let mut ingest = etk_asm::ingest::Ingest::new(&mut output, etk_ops::HardFork::Cancun); # ingest.ingest(file!(), src).unwrap(); # assert_eq!(output, &[0x60, 0x01]); ``` @@ -66,11 +68,12 @@ start: ```rust # extern crate etk_asm; +# extern crate etk_ops; # let src = r#" push4 selector("transfer(uint256,uint256)") # "#; # let mut output = Vec::new(); -# let mut ingest = etk_asm::ingest::Ingest::new(&mut output); +# let mut ingest = etk_asm::ingest::Ingest::new(&mut output, etk_ops::HardFork::Cancun); # ingest.ingest(file!(), src).unwrap(); # assert_eq!(output, &[0x63, 12, 247, 158, 10]); ``` @@ -83,6 +86,7 @@ Expressions support the following binary operators: ```rust # extern crate etk_asm; +# extern crate etk_ops; # let src = r#" push1 1+2 # addition push1 1*2 # multiplication @@ -91,7 +95,7 @@ push1 2/2 # division # "#; # let mut output = Vec::new(); -# let mut ingest = etk_asm::ingest::Ingest::new(&mut output); +# let mut ingest = etk_asm::ingest::Ingest::new(&mut output, etk_ops::HardFork::Cancun); # ingest.ingest(file!(), src).unwrap(); # assert_eq!(output, &[0x60, 0x03, 0x60, 0x02, 0x60, 0x01, 0x60, 0x01]); ``` diff --git a/doc/src/ch02-lang/ch03-labels.md b/doc/src/ch02-lang/ch03-labels.md index eb88ff86..6aba6396 100644 --- a/doc/src/ch02-lang/ch03-labels.md +++ b/doc/src/ch02-lang/ch03-labels.md @@ -4,6 +4,7 @@ Manually counting out jump destination addresses would be a monumentally pointle ```rust # extern crate etk_asm; +# extern crate etk_ops; # let src = r#" label0: # <- This is a label called "label0", ## and it has the value 0, since it is @@ -16,7 +17,7 @@ label0: # <- This is a label called "label0", jump # Now we jump to zero, which is a ## `jumpdest` instruction, looping forever. # "#; -# let mut ingest = etk_asm::ingest::Ingest::new(Vec::new()); +# let mut ingest = etk_asm::ingest::Ingest::new(Vec::new(), etk_ops::HardFork::Cancun); # ingest.ingest(file!(), src).unwrap(); ``` @@ -34,6 +35,7 @@ That's not all! You can also use labels to calculate lengths: ```rust # extern crate etk_asm; +# extern crate etk_ops; # let src = r#" push1 start push1 end @@ -46,7 +48,7 @@ start: pc end: # "#; -# let mut ingest = etk_asm::ingest::Ingest::new(Vec::new()); +# let mut ingest = etk_asm::ingest::Ingest::new(Vec::new(), etk_ops::HardFork::Cancun); # ingest.ingest(file!(), src).unwrap(); ``` diff --git a/doc/src/ch02-lang/ch04-macros/README.md b/doc/src/ch02-lang/ch04-macros/README.md index 4e19be8f..efaaff0b 100644 --- a/doc/src/ch02-lang/ch04-macros/README.md +++ b/doc/src/ch02-lang/ch04-macros/README.md @@ -12,6 +12,7 @@ An instruction macro looks like this: ```rust # extern crate etk_asm; +# extern crate etk_ops; # let src = r#" %macro push_sum(a, b) push1 $a + $b @@ -20,7 +21,7 @@ An instruction macro looks like this: %push_sum(4, 2) # "#; # let mut output = Vec::new(); -# let mut ingest = etk_asm::ingest::Ingest::new(&mut output); +# let mut ingest = etk_asm::ingest::Ingest::new(&mut output, etk_ops::HardFork::Cancun); # ingest.ingest(file!(), src).unwrap(); # assert_eq!(output, &[0x60, 0x06]); ``` @@ -37,6 +38,7 @@ Expression macros _do not_ begin with `%`, and cannot replace instructions. Inst ```rust # extern crate etk_asm; +# extern crate etk_ops; # let src = r#" %def add_one(num) $num+1 @@ -45,7 +47,7 @@ Expression macros _do not_ begin with `%`, and cannot replace instructions. Inst push1 add_one(41) # "#; # let mut output = Vec::new(); -# let mut ingest = etk_asm::ingest::Ingest::new(&mut output); +# let mut ingest = etk_asm::ingest::Ingest::new(&mut output, etk_ops::HardFork::Cancun); # ingest.ingest(file!(), src).unwrap(); # assert_eq!(output, &[0x60, 0x2a]); ``` diff --git a/doc/src/ch02-lang/ch04-macros/ch01-builtins.md b/doc/src/ch02-lang/ch04-macros/ch01-builtins.md index 05581df9..f8b1cc87 100644 --- a/doc/src/ch02-lang/ch04-macros/ch01-builtins.md +++ b/doc/src/ch02-lang/ch04-macros/ch01-builtins.md @@ -79,6 +79,7 @@ For example: ```rust # extern crate etk_asm; +# extern crate etk_ops; # let src = r#" %push(hello) @@ -86,7 +87,7 @@ hello: jumpdest # "#; # let mut output = Vec::new(); -# let mut ingest = etk_asm::ingest::Ingest::new(&mut output); +# let mut ingest = etk_asm::ingest::Ingest::new(&mut output, etk_ops::HardFork::Cancun); # ingest.ingest(file!(), src).unwrap(); # assert_eq!(output, &[0x60, 0x02, 0x5b]); ``` @@ -108,11 +109,12 @@ For example: ```rust # extern crate etk_asm; +# extern crate etk_ops; # let src = r#" push4 selector("transfer(address,uint256)") # <- expands to 0x63a9059cbb # "#; # let mut output = Vec::new(); -# let mut ingest = etk_asm::ingest::Ingest::new(&mut output); +# let mut ingest = etk_asm::ingest::Ingest::new(&mut output, etk_ops::HardFork::Cancun); # ingest.ingest(file!(), src).unwrap(); # assert_eq!(output, &[0x63, 0xa9, 0x05, 0x9c, 0xbb]); ``` @@ -131,11 +133,12 @@ For example: ```rust # extern crate etk_asm; +# extern crate etk_ops; # let src = r#" push32 topic("transfer(address,uint256)") # "#; # let mut output = Vec::new(); -# let mut ingest = etk_asm::ingest::Ingest::new(&mut output); +# let mut ingest = etk_asm::ingest::Ingest::new(&mut output, etk_ops::HardFork::Cancun); # ingest.ingest(file!(), src).unwrap(); # assert_eq!(output, &[0x7f, 169, 5, 156, 187, 42, 176, 158, 178, 25, 88, 63, 74, 89, 165, 208, 98, 58, 222, 52, 109, 150, 43, 205, 78, 70, 177, 29, 160, 71, 201, 4, 155]); ``` diff --git a/doc/src/ch02-lang/ch04-macros/ch02-expression.md b/doc/src/ch02-lang/ch04-macros/ch02-expression.md index 8be11f79..ef221384 100644 --- a/doc/src/ch02-lang/ch04-macros/ch02-expression.md +++ b/doc/src/ch02-lang/ch04-macros/ch02-expression.md @@ -8,6 +8,7 @@ Expression macros can accept an arbitrary number of parameters. Parameters are r ```rust # extern crate etk_asm; +# extern crate etk_ops; # let src = r#" %def my_macro() 42 @@ -17,7 +18,7 @@ Expression macros can accept an arbitrary number of parameters. Parameters are r $x+$y+$z %end # "#; -# let mut ingest = etk_asm::ingest::Ingest::new(Vec::new()); +# let mut ingest = etk_asm::ingest::Ingest::new(Vec::new(), etk_ops::HardFork::Cancun); # ingest.ingest(file!(), src).unwrap(); ``` @@ -27,6 +28,7 @@ Expression macros can be invoked anywhere an expression is expected. ```rust # extern crate etk_asm; +# extern crate etk_ops; # let src = r#" # %def my_macro() # 42 @@ -38,7 +40,7 @@ push1 my_macro() push1 sum(1, 2, my_macro()) # "#; # let mut output = Vec::new(); -# let mut ingest = etk_asm::ingest::Ingest::new(&mut output); +# let mut ingest = etk_asm::ingest::Ingest::new(&mut output, etk_ops::HardFork::Cancun); # ingest.ingest(file!(), src).unwrap(); # assert_eq!(output, &[0x60, 0x2a, 0x60, 0x2d]); ``` diff --git a/doc/src/ch02-lang/ch04-macros/ch03-instruction.md b/doc/src/ch02-lang/ch04-macros/ch03-instruction.md index 07dde030..9c29e04d 100644 --- a/doc/src/ch02-lang/ch04-macros/ch03-instruction.md +++ b/doc/src/ch02-lang/ch04-macros/ch03-instruction.md @@ -8,6 +8,7 @@ Instruction macros can accept an arbitrary number of parameters. Parameters are ```rust # extern crate etk_asm; +# extern crate etk_ops; # let src = r#" %macro my_macro() push1 42 @@ -17,7 +18,7 @@ Instruction macros can accept an arbitrary number of parameters. Parameters are push1 $x+$y+$z %end # "#; -# let mut ingest = etk_asm::ingest::Ingest::new(Vec::new()); +# let mut ingest = etk_asm::ingest::Ingest::new(Vec::new(), etk_ops::HardFork::Cancun); # ingest.ingest(file!(), src).unwrap(); ``` @@ -27,6 +28,7 @@ Expression macros can be invoked anywhere an instruction is expected. ```rust # extern crate etk_asm; +# extern crate etk_ops; # let src = r#" # %macro my_macro() # push1 42 @@ -38,7 +40,7 @@ Expression macros can be invoked anywhere an instruction is expected. %sum(1, 2, 3) # "#; # let mut output = Vec::new(); -# let mut ingest = etk_asm::ingest::Ingest::new(&mut output); +# let mut ingest = etk_asm::ingest::Ingest::new(&mut output, etk_ops::HardFork::Cancun); # ingest.ingest(file!(), src).unwrap(); # assert_eq!(output, &[0x60, 0x2a, 0x60, 0x06]); ``` From 6dcd4715e141508cad16d249aaeeb21fb991d2bc Mon Sep 17 00:00:00 2001 From: Gaston Zanitti Date: Mon, 23 Oct 2023 18:20:56 -0300 Subject: [PATCH 44/73] Missing ; --- doc/src/ch02-lang/ch02-expressions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/src/ch02-lang/ch02-expressions.md b/doc/src/ch02-lang/ch02-expressions.md index b66c4f88..fa195f16 100644 --- a/doc/src/ch02-lang/ch02-expressions.md +++ b/doc/src/ch02-lang/ch02-expressions.md @@ -10,7 +10,7 @@ While an assembled `push` must have a concrete value, it is often useful when de ```rust # extern crate etk_asm; -# extern crate etk_ops +# extern crate etk_ops; # let src = r#" push1 1+(2*3)/4 # "#; From 265aa0adf015a2ee2edd0b1629a01ab4a26e4cf7 Mon Sep 17 00:00:00 2001 From: Gaston Zanitti Date: Wed, 25 Oct 2023 16:38:27 -0300 Subject: [PATCH 45/73] Progress with new macro %hardfork("...") --- etk-asm/src/ast.rs | 2 + etk-asm/src/ingest.rs | 42 +++++ etk-asm/src/parse/asm.pest | 13 +- etk-asm/src/parse/error.rs | 21 +++ etk-asm/src/parse/macros.rs | 31 +++- etk-asm/tests/asm.rs | 33 ++++ .../tests/asm/hardfork/invalid-hardfork.etk | 1 + etk-asm/tests/asm/hardfork/invalid-range.etk | 1 + etk-asm/tests/asm/hardfork/valid-hardfork.etk | 1 + etk-ops/src/lib.rs | 157 ++++++++++++++++-- 10 files changed, 287 insertions(+), 15 deletions(-) create mode 100644 etk-asm/tests/asm/hardfork/invalid-hardfork.etk create mode 100644 etk-asm/tests/asm/hardfork/invalid-range.etk create mode 100644 etk-asm/tests/asm/hardfork/valid-hardfork.etk diff --git a/etk-asm/src/ast.rs b/etk-asm/src/ast.rs index dd348f4f..ea1278ac 100644 --- a/etk-asm/src/ast.rs +++ b/etk-asm/src/ast.rs @@ -4,6 +4,7 @@ use crate::ops::{Abstract, AbstractOp, ExpressionMacroDefinition, InstructionMac use etk_ops::cancun::Op as CancunOp; use etk_ops::london::Op as LondonOp; use etk_ops::shanghai::Op as ShanghaiOp; +use etk_ops::HardForkDirective; #[derive(Debug, Clone, PartialEq)] pub(crate) enum Node { @@ -11,6 +12,7 @@ pub(crate) enum Node { Import(PathBuf), Include(PathBuf), IncludeHex(PathBuf), + HardforkMacro((HardForkDirective, Option)), } impl From> for Node { fn from(op: CancunOp) -> Self { diff --git a/etk-asm/src/ingest.rs b/etk-asm/src/ingest.rs index 00a85dd3..94b5b189 100644 --- a/etk-asm/src/ingest.rs +++ b/etk-asm/src/ingest.rs @@ -5,6 +5,8 @@ mod error { use crate::asm::Error as AssembleError; use crate::ParseError; + use etk_ops::HardFork; + use etk_ops::HardForkDirective; use snafu::{Backtrace, Snafu}; use std::path::PathBuf; @@ -93,6 +95,22 @@ mod error { /// The location of the error. backtrace: Backtrace, }, + + /// Hardfork set for compilation is out of range. + #[snafu(display( + "Hardfork set for compilation was `{}` but `{}` is required.", + hardfork, + directive + ))] + #[non_exhaustive] + OutOfRangeHardfork { + /// Hardfork set for compilation. + hardfork: HardFork, + /// Directive that breaks the compilation. + directive: HardForkDirective, + /// The location of the error. + backtrace: Backtrace, + }, } } @@ -344,6 +362,30 @@ where raws.push(RawOp::Raw(raw)) } + Node::HardforkMacro(directive) => { + // Here, directive is always a valid range. + let (hfd1, ophfd2) = directive; + ensure!( + self.hardfork.is_valid(&hfd1), + error::OutOfRangeHardfork { + hardfork: self.hardfork.clone(), + directive: hfd1, + } + ); + + match ophfd2 { + Some(hfd2) => { + ensure!( + self.hardfork.is_valid(&hfd2), + error::OutOfRangeHardfork { + hardfork: self.hardfork.clone(), + directive: hfd2, + } + ); + } + None => {} + } + } } } diff --git a/etk-asm/src/parse/asm.pest b/etk-asm/src/parse/asm.pest index 19c9f1bd..6ca4c184 100644 --- a/etk-asm/src/parse/asm.pest +++ b/etk-asm/src/parse/asm.pest @@ -40,17 +40,28 @@ instruction_macro_variable = @{ "$" ~ function_parameter } instruction_macro = !{ "%" ~ function_invocation } local_macro = { !builtin ~ (instruction_macro_definition | instruction_macro | expression_macro_definition) } -builtin = ${ "%" ~ ( import | include | include_hex | push_macro ) } +builtin = ${ "%" ~ (import | include | include_hex | push_macro | hardfork) } import = !{ "import" ~ arguments } include = !{ "include" ~ arguments } include_hex = !{ "include_hex" ~ arguments } push_macro = !{ "push" ~ arguments } +hardfork = !{"hardfork" ~ hardfork_arguments } arguments = _{ "(" ~ arguments_list? ~ ")" } arguments_list = _{ ( argument ~ "," )* ~ argument? } argument = _{ string | expression } +hardfork_arguments = _{ "(" ~ hardfork_arguments_list ~ ")" } +hardfork_arguments_list = _{ "\"" ~ ( hardfork_argument ~ "," )* ~ hardfork_argument? ~ "\"" } +hardfork_argument = { hardfork_operator? ~ hardfork_name } +hardfork_operator = _{ lte | gte | gt | lt } +gt = { ">" } +lt = { "<" } +gte = { ">=" } +lte = { "<=" } +hardfork_name = { "london" | "cancun" } + string = @{ "\"" ~ string_char* ~ "\"" } string_char = _{ "\\\\" | "\\\"" | (!"\\" ~ !"\"" ~ ANY) } diff --git a/etk-asm/src/parse/error.rs b/etk-asm/src/parse/error.rs index a60dcf13..25346250 100644 --- a/etk-asm/src/parse/error.rs +++ b/etk-asm/src/parse/error.rs @@ -87,6 +87,27 @@ pub enum ParseError { /// The location of the error. backtrace: Backtrace, }, + + /// Hardfork defined inside macro is invalid. + #[snafu(display("hardfork `{}` is invalid", hardfork))] + #[non_exhaustive] + InvalidHardfork { + /// Name of the invalid hardfork. + hardfork: String, + + /// The location of the error. + backtrace: Backtrace, + }, + + /// Range of Hardforks define inside macro is invalid. + #[snafu(display("Expected range of two hardfork max, but got {}.", parsed))] + #[non_exhaustive] + InvalidRangeHardfork { + /// Number of hardforks parsed. + parsed: usize, + /// The location of the error. + backtrace: Backtrace, + }, } impl From> for ParseError { diff --git a/etk-asm/src/parse/macros.rs b/etk-asm/src/parse/macros.rs index 2e157ec1..15a003ac 100644 --- a/etk-asm/src/parse/macros.rs +++ b/etk-asm/src/parse/macros.rs @@ -7,7 +7,8 @@ use crate::ops::{ AbstractOp, Expression, ExpressionMacroDefinition, ExpressionMacroInvocation, InstructionMacroDefinition, InstructionMacroInvocation, }; -use etk_ops::HardFork; +use crate::parse::error::InvalidRangeHardfork; +use etk_ops::{HardFork, HardForkDirective}; use pest::iterators::Pair; use std::path::PathBuf; @@ -46,6 +47,34 @@ pub(crate) fn parse_builtin(pair: Pair) -> Result { let expr = expression::parse(pair.into_inner().next().unwrap())?; Node::Op(AbstractOp::Push(expr.into())) } + Rule::hardfork => { + let mut directives = Vec::new(); + for inner in pair.into_inner() { + let mut directive = inner.into_inner(); + let operator = match directive.next() { + Some(operator) => { + let operator = operator.as_str().into(); + Some(operator) + } + None => None, + }; + + let hardfork = directive.next().unwrap().as_str().into(); + println!("hardfork: {:?}", hardfork); + println!("operator: {:?}", operator); + directives.push(HardForkDirective { operator, hardfork }); + if directives.len() > 2 { + return InvalidRangeHardfork { + parsed: directives.len(), + } + .fail(); + } + } + + directives.reverse(); + let tuple = (directives.pop().unwrap(), directives.pop()); + Node::HardforkMacro(tuple) + } _ => unreachable!(), }; diff --git a/etk-asm/tests/asm.rs b/etk-asm/tests/asm.rs index 86e62212..e2c142c2 100644 --- a/etk-asm/tests/asm.rs +++ b/etk-asm/tests/asm.rs @@ -304,3 +304,36 @@ fn test_dynamic_push_and_include() -> Result<(), Error> { Ok(()) } + +#[test] +fn test_valid_hardfork() -> Result<(), Error> { + let mut output = Vec::new(); + let mut ingester = Ingest::new(&mut output, HardFork::Cancun); + ingester.ingest_file(source(&["hardfork", "valid-hardfork.etk"]))?; + + assert_eq!(output, hex!("")); + + Ok(()) +} + +#[test] +fn test_invalid_hardfork() -> Result<(), Error> { + let mut output = Vec::new(); + let mut ingester = Ingest::new(&mut output, HardFork::Cancun); + ingester.ingest_file(source(&["hardfork", "invalid-hardfork.etk"]))?; + + assert_eq!(output, hex!("")); + + Ok(()) +} + +#[test] +fn test_invalid_range_hardfork() -> Result<(), Error> { + let mut output = Vec::new(); + let mut ingester = Ingest::new(&mut output, HardFork::Cancun); + ingester.ingest_file(source(&["hardfork", "invalid-range.etk"]))?; + + assert_eq!(output, hex!("")); + + Ok(()) +} diff --git a/etk-asm/tests/asm/hardfork/invalid-hardfork.etk b/etk-asm/tests/asm/hardfork/invalid-hardfork.etk new file mode 100644 index 00000000..8eb4da4a --- /dev/null +++ b/etk-asm/tests/asm/hardfork/invalid-hardfork.etk @@ -0,0 +1 @@ +%hardfork(">=buenosaires") \ No newline at end of file diff --git a/etk-asm/tests/asm/hardfork/invalid-range.etk b/etk-asm/tests/asm/hardfork/invalid-range.etk new file mode 100644 index 00000000..e15de07a --- /dev/null +++ b/etk-asm/tests/asm/hardfork/invalid-range.etk @@ -0,0 +1 @@ +%hardfork(">=cancun,<=london") \ No newline at end of file diff --git a/etk-asm/tests/asm/hardfork/valid-hardfork.etk b/etk-asm/tests/asm/hardfork/valid-hardfork.etk new file mode 100644 index 00000000..b69ae5e9 --- /dev/null +++ b/etk-asm/tests/asm/hardfork/valid-hardfork.etk @@ -0,0 +1 @@ +%hardfork(">london,<=cancun") \ No newline at end of file diff --git a/etk-ops/src/lib.rs b/etk-ops/src/lib.rs index 498b5adc..e76301b6 100644 --- a/etk-ops/src/lib.rs +++ b/etk-ops/src/lib.rs @@ -14,6 +14,9 @@ use snafu::{Backtrace, Snafu}; use std::borrow::{Borrow, BorrowMut}; +use std::fmt::Display; +use std::str::FromStr; + /// Trait for types that represent an EVM instruction. pub trait Operation { /// The return type of [`Operation::code`]. @@ -409,16 +412,37 @@ impl std::fmt::Display for HardForkOp<()> { } /// Hard forks of the Ethereum Virtual Machine. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq)] pub enum HardFork { - /// The Cancun hard fork. - Cancun, + /// The London hard fork. + London, /// The Shanghai hard fork. Shanghai, - /// The London hard fork. - London, + /// The Cancun hard fork. + Cancun, +} + +impl HardFork { + /// Returns true if the given hardfork directive is valid for this hardfork. + pub fn is_valid(&self, hfd: &HardForkDirective) -> bool { + match self { + HardFork::London => self.check_hardfork_operator(Self::London, hfd), + HardFork::Shanghai => self.check_hardfork_operator(Self::London, hfd), + HardFork::Cancun => self.check_hardfork_operator(Self::London, hfd), + } + } + + fn check_hardfork_operator(&self, hardfork: HardFork, directive: &HardForkDirective) -> bool { + match directive.operator { + Some(OperatorDirective::GreaterThan) => directive.hardfork > hardfork, + Some(OperatorDirective::GreaterThanOrEqual) => directive.hardfork >= hardfork, + Some(OperatorDirective::LessThan) => directive.hardfork < hardfork, + Some(OperatorDirective::LessThanOrEqual) => directive.hardfork <= hardfork, + None => directive.hardfork == hardfork, + } + } } impl Default for HardFork { @@ -427,18 +451,125 @@ impl Default for HardFork { } } -use std::str::FromStr; +impl Display for HardFork { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + Self::London => write!(f, "london"), + Self::Shanghai => write!(f, "shanghai"), + Self::Cancun => write!(f, "cancun"), + } + } +} + +impl From<&str> for HardFork { + fn from(s: &str) -> Self { + match s { + "london" => Self::London, + "shanghai" => Self::Shanghai, + "cancun" => Self::Cancun, + _ => panic!("Invalid hardfork: {}", s), + } + } +} impl FromStr for HardFork { - type Err = String; + type Err = FromStrError; fn from_str(s: &str) -> Result { - let s = s.to_lowercase(); - match s.as_str() { - "cancun" => Ok(Self::Cancun), - "shanghai" => Ok(Self::Shanghai), - "london" => Ok(Self::London), - _ => Err(format!("Invalid hardfork: {}", s)), + Ok(Self::from(s)) + } +} + +/// A directive that specifies a hardfork. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct HardForkDirective { + /// The operator used to define a range of hardforks. + pub operator: Option, + + /// The hardfork. + pub hardfork: HardFork, +} + +impl Display for HardForkDirective { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match &self.operator { + Some(op) => write!(f, "{}{}", op, self.hardfork), + None => write!(f, "{}", self.hardfork), + } + } +} + +impl PartialOrd> for HardForkDirective { + fn partial_cmp(&self, other: &Option) -> Option { + match other { + Some(_) => self.partial_cmp(other), + None => Some(std::cmp::Ordering::Greater), + } + } +} + +impl PartialEq> for HardForkDirective { + fn eq(&self, other: &Option) -> bool { + match other { + Some(hfd) => self.eq(hfd), + None => false, + } + } +} + +impl PartialOrd for Option { + fn partial_cmp(&self, other: &HardForkDirective) -> Option { + match self { + Some(hfd) => hfd.partial_cmp(&Some(other.to_owned())), + None => Some(std::cmp::Ordering::Less), + } + } +} + +impl PartialEq for Option { + fn eq(&self, other: &HardForkDirective) -> bool { + match self { + Some(hfd) => hfd.eq(other), + None => false, + } + } +} + +/// An operator used to define a range of hardforks. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum OperatorDirective { + /// The `>=` operator. + GreaterThan, + + /// The `<=` operator. + GreaterThanOrEqual, + + /// The `>` operator. + LessThan, + + /// The `<` operator. + LessThanOrEqual, +} + +impl From<&str> for OperatorDirective { + fn from(s: &str) -> Self { + match s { + ">=" => Self::GreaterThanOrEqual, + "<=" => Self::LessThanOrEqual, + ">" => Self::GreaterThan, + "<" => Self::LessThan, + _ => panic!("Invalid operator: {}", s), + } + } +} + +impl Display for OperatorDirective { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + Self::GreaterThanOrEqual => write!(f, ">="), + Self::LessThanOrEqual => write!(f, "<="), + Self::GreaterThan => write!(f, ">"), + Self::LessThan => write!(f, "<"), } } } From 4e472c9ee042eea8023a7d50e3dc5c25b02e7c66 Mon Sep 17 00:00:00 2001 From: Gaston Zanitti Date: Thu, 26 Oct 2023 16:34:23 -0300 Subject: [PATCH 46/73] Better error reporting. More tests --- etk-asm/src/parse/asm.pest | 2 +- etk-asm/src/parse/error.rs | 49 +++++++++++++++++++- etk-asm/src/parse/macros.rs | 90 +++++++++++++++++++++++++++++++++---- etk-asm/tests/asm.rs | 28 ++++++++---- etk-ops/src/lib.rs | 47 +++++++++---------- 5 files changed, 170 insertions(+), 46 deletions(-) diff --git a/etk-asm/src/parse/asm.pest b/etk-asm/src/parse/asm.pest index 6ca4c184..2ddb1770 100644 --- a/etk-asm/src/parse/asm.pest +++ b/etk-asm/src/parse/asm.pest @@ -60,7 +60,7 @@ gt = { ">" } lt = { "<" } gte = { ">=" } lte = { "<=" } -hardfork_name = { "london" | "cancun" } +hardfork_name = { string_char* } string = @{ "\"" ~ string_char* ~ "\"" } string_char = _{ "\\\\" | "\\\"" | (!"\\" ~ !"\"" ~ ANY) } diff --git a/etk-asm/src/parse/error.rs b/etk-asm/src/parse/error.rs index 25346250..c6223564 100644 --- a/etk-asm/src/parse/error.rs +++ b/etk-asm/src/parse/error.rs @@ -1,5 +1,6 @@ use std::path::PathBuf; +use etk_ops::HardForkDirective; use pest::error::Error; use snafu::{Backtrace, IntoError, Snafu}; @@ -99,15 +100,59 @@ pub enum ParseError { backtrace: Backtrace, }, - /// Range of Hardforks define inside macro is invalid. + /// Range of Hardforks exceeded max amout. #[snafu(display("Expected range of two hardfork max, but got {}.", parsed))] #[non_exhaustive] - InvalidRangeHardfork { + ExceededRangeHardfork { /// Number of hardforks parsed. parsed: usize, /// The location of the error. backtrace: Backtrace, }, + + /// Range of hardforks is invalid + #[snafu(display( + "For a range, both hardforks needs to have operators: {},{}.", + directive0, + directive1 + ))] + #[non_exhaustive] + InvalidRangeHardfork { + /// Directive with invalid range. + directive0: HardForkDirective, + /// Directive with invalid range. + directive1: HardForkDirective, + /// The location of the error. + backtrace: Backtrace, + }, + + /// Range of hardforks overlap and should be simplified. + #[snafu(display( + "Range of hardforks overlap and should be simplified: {},{}.", + directive0, + directive1 + ))] + #[non_exhaustive] + OverlappingRangeHardfork { + /// Directive with invalid range. + directive0: HardForkDirective, + /// Directive with invalid range. + directive1: HardForkDirective, + /// The location of the error. + backtrace: Backtrace, + }, + + /// Range of hardforks is empty. + #[snafu(display("Range of hardforks is empty: {},{}.", directive0, directive1))] + #[non_exhaustive] + EmptyRangeHardfork { + /// Directive with invalid range. + directive0: HardForkDirective, + /// Directive with invalid range. + directive1: HardForkDirective, + /// The location of the error. + backtrace: Backtrace, + }, } impl From> for ParseError { diff --git a/etk-asm/src/parse/macros.rs b/etk-asm/src/parse/macros.rs index 15a003ac..e02881c7 100644 --- a/etk-asm/src/parse/macros.rs +++ b/etk-asm/src/parse/macros.rs @@ -1,5 +1,5 @@ use super::args::Signature; -use super::error::ParseError; +use super::error::{EmptyRangeHardfork, OverlappingRangeHardfork, ParseError}; use super::expression; use super::parser::Rule; use crate::ast::Node; @@ -7,8 +7,8 @@ use crate::ops::{ AbstractOp, Expression, ExpressionMacroDefinition, ExpressionMacroInvocation, InstructionMacroDefinition, InstructionMacroInvocation, }; -use crate::parse::error::InvalidRangeHardfork; -use etk_ops::{HardFork, HardForkDirective}; +use crate::parse::error::{ExceededRangeHardfork, InvalidHardfork, InvalidRangeHardfork}; +use etk_ops::{HardFork, HardForkDirective, OperatorDirective}; use pest::iterators::Pair; use std::path::PathBuf; @@ -59,12 +59,26 @@ pub(crate) fn parse_builtin(pair: Pair) -> Result { None => None, }; - let hardfork = directive.next().unwrap().as_str().into(); - println!("hardfork: {:?}", hardfork); - println!("operator: {:?}", operator); - directives.push(HardForkDirective { operator, hardfork }); + // Tried moving this to into() but can't manage invalid hardforks. + let hardforkstr = directive.next().unwrap().as_str(); + let hardfork = match hardforkstr { + "london" => HardFork::London, + "shanghai" => HardFork::Shanghai, + "cancun" => HardFork::Cancun, + _ => { + return InvalidHardfork { + hardfork: hardforkstr, + } + .fail() + } + }; + + directives.push(HardForkDirective { + operator, + hardfork: hardfork, + }); if directives.len() > 2 { - return InvalidRangeHardfork { + return ExceededRangeHardfork { parsed: directives.len(), } .fail(); @@ -72,6 +86,7 @@ pub(crate) fn parse_builtin(pair: Pair) -> Result { } directives.reverse(); + hardfork_in_valid_range(&directives)?; let tuple = (directives.pop().unwrap(), directives.pop()); Node::HardforkMacro(tuple) } @@ -81,6 +96,65 @@ pub(crate) fn parse_builtin(pair: Pair) -> Result { Ok(node) } +fn hardfork_in_valid_range(directives: &[HardForkDirective]) -> Result<(), ParseError> { + if directives.len() == 1 { + return Ok(()); + } + + let mut decresing_bound: Option = None; + let mut incresing_bound: Option = None; + + for directive in directives { + match directive.operator { + Some(OperatorDirective::LessThan) | Some(OperatorDirective::LessThanOrEqual) => { + match decresing_bound { + Some(_) => { + return OverlappingRangeHardfork { + directive0: directives.get(0), + directive1: directives.get(1), + } + .fail(); + } + None => { + decresing_bound = Some(directive.clone()); + } + } + } + Some(OperatorDirective::GreaterThan) | Some(OperatorDirective::GreaterThanOrEqual) => { + match incresing_bound { + Some(_) => { + return OverlappingRangeHardfork { + directive0: directives.get(0), + directive1: directives.get(1), + } + .fail(); + } + None => { + incresing_bound = Some(directive.clone()); + } + } + } + None => { + return InvalidRangeHardfork { + directive0: directives.get(0), + directive1: directives.get(1), + } + .fail(); + } + } + } + + if incresing_bound.unwrap().hardfork > decresing_bound.unwrap().hardfork { + return EmptyRangeHardfork { + directive0: directives.get(0), + directive1: directives.get(1), + } + .fail(); + } + + Ok(()) +} + fn parse_instruction_macro_defn( pair: Pair, hardfork: HardFork, diff --git a/etk-asm/tests/asm.rs b/etk-asm/tests/asm.rs index e2c142c2..bb0a66e7 100644 --- a/etk-asm/tests/asm.rs +++ b/etk-asm/tests/asm.rs @@ -317,23 +317,33 @@ fn test_valid_hardfork() -> Result<(), Error> { } #[test] -fn test_invalid_hardfork() -> Result<(), Error> { +fn test_invalid_hardfork() { let mut output = Vec::new(); let mut ingester = Ingest::new(&mut output, HardFork::Cancun); - ingester.ingest_file(source(&["hardfork", "invalid-hardfork.etk"]))?; + let err = ingester + .ingest_file(source(&["hardfork", "invalid-hardfork.etk"])) + .unwrap_err(); - assert_eq!(output, hex!("")); + println!("{:?}", err); - Ok(()) + assert_matches!(err, etk_asm::ingest::Error::Parse { source: + etk_asm::ParseError::InvalidHardfork { hardfork, .. }, .. } + if hardfork == "buenosaires".to_string()); } #[test] -fn test_invalid_range_hardfork() -> Result<(), Error> { +fn test_invalid_range_hardfork() { let mut output = Vec::new(); let mut ingester = Ingest::new(&mut output, HardFork::Cancun); - ingester.ingest_file(source(&["hardfork", "invalid-range.etk"]))?; - - assert_eq!(output, hex!("")); + let err = ingester + .ingest_file(source(&["hardfork", "invalid-range.etk"])) + .unwrap_err(); - Ok(()) + assert_matches!( + err, + etk_asm::ingest::Error::Parse { + source: etk_asm::ParseError::EmptyRangeHardfork { .. }, + .. + } + ); } diff --git a/etk-ops/src/lib.rs b/etk-ops/src/lib.rs index e76301b6..f25298b2 100644 --- a/etk-ops/src/lib.rs +++ b/etk-ops/src/lib.rs @@ -426,21 +426,13 @@ pub enum HardFork { impl HardFork { /// Returns true if the given hardfork directive is valid for this hardfork. - pub fn is_valid(&self, hfd: &HardForkDirective) -> bool { - match self { - HardFork::London => self.check_hardfork_operator(Self::London, hfd), - HardFork::Shanghai => self.check_hardfork_operator(Self::London, hfd), - HardFork::Cancun => self.check_hardfork_operator(Self::London, hfd), - } - } - - fn check_hardfork_operator(&self, hardfork: HardFork, directive: &HardForkDirective) -> bool { + pub fn is_valid(&self, directive: &HardForkDirective) -> bool { match directive.operator { - Some(OperatorDirective::GreaterThan) => directive.hardfork > hardfork, - Some(OperatorDirective::GreaterThanOrEqual) => directive.hardfork >= hardfork, - Some(OperatorDirective::LessThan) => directive.hardfork < hardfork, - Some(OperatorDirective::LessThanOrEqual) => directive.hardfork <= hardfork, - None => directive.hardfork == hardfork, + Some(OperatorDirective::GreaterThan) => self > &directive.hardfork, + Some(OperatorDirective::GreaterThanOrEqual) => self >= &directive.hardfork, + Some(OperatorDirective::LessThan) => self < &directive.hardfork, + Some(OperatorDirective::LessThanOrEqual) => self <= &directive.hardfork, + None => self == &directive.hardfork, } } } @@ -461,22 +453,16 @@ impl Display for HardFork { } } -impl From<&str> for HardFork { - fn from(s: &str) -> Self { - match s { - "london" => Self::London, - "shanghai" => Self::Shanghai, - "cancun" => Self::Cancun, - _ => panic!("Invalid hardfork: {}", s), - } - } -} - impl FromStr for HardFork { type Err = FromStrError; fn from_str(s: &str) -> Result { - Ok(Self::from(s)) + match s { + "london" => Ok(HardFork::London), + "shanghai" => Ok(HardFork::Shanghai), + "cancun" => Ok(HardFork::Cancun), + _ => FromStrSnafu { mnemonic: s }.fail(), + } } } @@ -499,6 +485,15 @@ impl Display for HardForkDirective { } } +impl Into for Option<&HardForkDirective> { + fn into(self) -> HardForkDirective { + match self { + Some(hfd) => hfd.to_owned(), + None => panic!("Cannot convert None into HardForkDirective"), + } + } +} + impl PartialOrd> for HardForkDirective { fn partial_cmp(&self, other: &Option) -> Option { match other { From 3fecdd19ceb720609831490903b150ca384ac2ad Mon Sep 17 00:00:00 2001 From: Gaston Zanitti Date: Thu, 26 Oct 2023 17:40:48 -0300 Subject: [PATCH 47/73] Limit hardforks to ASCII_ALPHA --- etk-asm/src/parse/asm.pest | 2 +- etk-asm/src/parse/macros.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/etk-asm/src/parse/asm.pest b/etk-asm/src/parse/asm.pest index 2ddb1770..3547fa19 100644 --- a/etk-asm/src/parse/asm.pest +++ b/etk-asm/src/parse/asm.pest @@ -60,7 +60,7 @@ gt = { ">" } lt = { "<" } gte = { ">=" } lte = { "<=" } -hardfork_name = { string_char* } +hardfork_name = { ASCII_ALPHA* } string = @{ "\"" ~ string_char* ~ "\"" } string_char = _{ "\\\\" | "\\\"" | (!"\\" ~ !"\"" ~ ANY) } diff --git a/etk-asm/src/parse/macros.rs b/etk-asm/src/parse/macros.rs index e02881c7..58fd75eb 100644 --- a/etk-asm/src/parse/macros.rs +++ b/etk-asm/src/parse/macros.rs @@ -53,8 +53,8 @@ pub(crate) fn parse_builtin(pair: Pair) -> Result { let mut directive = inner.into_inner(); let operator = match directive.next() { Some(operator) => { - let operator = operator.as_str().into(); - Some(operator) + let op = operator.as_str().into(); + Some(op) } None => None, }; From 12c609c081bc96812b067b12a45192c1f700e3c9 Mon Sep 17 00:00:00 2001 From: Gaston Zanitti Date: Tue, 12 Sep 2023 15:41:08 -0300 Subject: [PATCH 48/73] Assembler simplification --- etk-analyze/src/bin/ecfg.rs | 2 +- etk-asm/src/asm.rs | 877 ++++++++---------- etk-asm/src/ast.rs | 5 +- etk-asm/src/ingest.rs | 255 ++--- etk-asm/src/parse/mod.rs | 1 + etk-asm/tests/asm.rs | 27 + etk-asm/tests/asm/every-op/test.etk | 5 + .../undefined-label-undefined-macro.etk | 6 + etk-asm/tests/asm/variable-push/included.etk | 594 ++++++++++++ etk-asm/tests/asm/variable-push/main.etk | 7 + etk-dasm/src/bin/disease.rs | 5 +- 11 files changed, 1107 insertions(+), 677 deletions(-) create mode 100644 etk-asm/tests/asm/every-op/test.etk create mode 100644 etk-asm/tests/asm/instruction-macro/undefined-label-undefined-macro.etk create mode 100644 etk-asm/tests/asm/variable-push/included.etk create mode 100644 etk-asm/tests/asm/variable-push/main.etk diff --git a/etk-analyze/src/bin/ecfg.rs b/etk-analyze/src/bin/ecfg.rs index a8fd7a37..7f963c3b 100644 --- a/etk-analyze/src/bin/ecfg.rs +++ b/etk-analyze/src/bin/ecfg.rs @@ -58,7 +58,7 @@ fn run() -> Result<(), Error> { let blocks = separator .take() .into_iter() - .chain(separator.finish().into_iter()) + .chain(separator.finish()) .map(|x| AnnotatedBlock::annotate(&x)); let mut cfg = ControlFlowGraph::new(blocks); diff --git a/etk-asm/src/asm.rs b/etk-asm/src/asm.rs index a0555961..3e5556db 100644 --- a/etk-asm/src/asm.rs +++ b/etk-asm/src/asm.rs @@ -126,45 +126,42 @@ mod error { #[snafu(backtrace)] source: ParseError, }, + + /// An instruction macro was used without being defined. + #[snafu(display("variable `{}` inside macro, was never defined", var))] + #[non_exhaustive] + UndeclaredVariableMacro { + /// The variable that was used without being defined. + var: String, + + /// The location of the error. + backtrace: Backtrace, + }, } } pub use self::error::Error; -use crate::ops::expression::{self, Terminal}; -use crate::ops::{self, AbstractOp, Assemble, Expression, Imm, MacroDefinition}; -use etk_ops::cancun::Op; +use crate::ops::expression::Error::{UndefinedVariable, UnknownLabel, UnknownMacro}; +use crate::ops::{self, AbstractOp, Assemble, Expression, MacroDefinition}; use rand::Rng; -use snafu::OptionExt; -use std::collections::{hash_map, HashMap, HashSet, VecDeque}; +use std::cmp; +use std::collections::{hash_map, HashMap}; -/// An item to be assembled, which can be either an [`AbstractOp`] or a raw byte -/// sequence. +/// An item to be assembled, which can be either an [`AbstractOp`], +/// the inclusion of a new scope or a raw byte sequence. #[derive(Debug, Clone)] pub enum RawOp { /// An instruction to be assembled. Op(AbstractOp), + /// A new scope to be created with its corresponding list of operations. + Scope(Vec), + /// Raw bytes, for example from `%include_hex`, to be included verbatim in /// the output. Raw(Vec), } -impl RawOp { - fn size(&self) -> Option { - match self { - Self::Op(op) => op.size(), - Self::Raw(raw) => Some(raw.len()), - } - } - - fn expr(&self) -> Option<&Expression> { - match self { - Self::Op(op) => op.expr(), - Self::Raw(_) => None, - } - } -} - impl From for RawOp { fn from(op: AbstractOp) -> Self { Self::Op(op) @@ -190,52 +187,42 @@ impl From> for RawOp { /// # /// # use hex_literal::hex; /// let mut asm = Assembler::new(); -/// asm.push_all(vec![ +/// let result = asm.assemble(vec![ /// AbstractOp::new(GetPc), /// ])?; -/// let output = asm.take(); -/// asm.finish()?; -/// # assert_eq!(output, hex!("58")); +/// # assert_eq!(result, hex!("58")); /// # Result::<(), Error>::Ok(()) /// ``` -#[derive(Debug)] +#[derive(Debug, Default)] pub struct Assembler { - /// Assembled ops, ready to be taken. - ready: Vec, + /// Assembled ops. + ready: Vec, - /// Ops that cannot be encoded yet. - pending: VecDeque, - - /// Sum of the size of all the ops in `pending`, or `None` if `pending` contains - /// an unsized op. - pending_len: Option, - - /// Total number of `u8` that have been appended to `ready`. + /// Number of bytes used by the operations in `ready``. concrete_len: usize, - /// Labels, in `pending`, associated with an `AbstractOp::Label`. + /// Labels associated with an `AbstractOp::Label`. declared_labels: HashMap>, - /// Macros, in `pending`, associated with an `AbstractOp::Macro`. + /// Macros associated with an `AbstractOp::Macro`. declared_macros: HashMap, - /// Labels, in `pending`, that have been referred to (ex. with push) but + /// Labels that have been referred to (ex. with push) but /// have not been declared with an `AbstractOp::Label`. - undeclared_labels: HashSet, + undeclared_labels: Vec, } -impl Default for Assembler { - fn default() -> Self { - Self { - ready: Default::default(), - pending: Default::default(), - pending_len: Some(0), - concrete_len: 0, - declared_labels: Default::default(), - declared_macros: Default::default(), - undeclared_labels: Default::default(), - } - } +/// Struct used to keep track of pending label invocations and their positions in code. +#[derive(Debug, Clone)] +struct PendingLabel { + /// The name of the label. + label: String, + + /// Position where the label was invoked. + position: usize, + + /// Whether the label was invoked with a dynamic push or not. + dynamic_push: bool, } impl Assembler { @@ -244,107 +231,127 @@ impl Assembler { Self::default() } - /// Indicate that the input sequence is complete. Returns any errors that - /// may remain. - pub fn finish(self) -> Result<(), Error> { - if let Some(undef) = self.pending.front() { - return match undef { - RawOp::Op(AbstractOp::Macro(invc)) => error::UndeclaredInstructionMacro { - name: invc.name.clone(), - } - .fail(), - RawOp::Op(op) => { - match op - .clone() - .concretize((&self.declared_labels, &self.declared_macros).into()) - { - Ok(_) => unreachable!(), - Err(ops::Error::ContextIncomplete { - source: expression::Error::UnknownMacro { name, .. }, - .. - }) => error::UndeclaredExpressionMacro { name }.fail(), - Err(ops::Error::ContextIncomplete { - source: expression::Error::UnknownLabel { .. }, - .. - }) => { - let labels = op.expr().unwrap().labels(&self.declared_macros).unwrap(); - let declared = self.declared_labels.into_keys().collect(); - let invoked: HashSet<_> = labels.into_iter().collect(); - let missing = invoked - .difference(&declared) - .cloned() - .collect::>(); - error::UndeclaredLabels { labels: missing }.fail() + /// Inspect the macros in a series of [`RawOp`] and declare them in the assembler. + fn inspect_macros(&mut self, nodes: &I) -> Result<(), Error> + where + I: IntoIterator + Clone, + O: Into, + { + for op in nodes.clone() { + let op = op.into(); + if let RawOp::Op(AbstractOp::MacroDefinition(_)) = op { + self.declare_macro(op)? + } + } + + Ok(()) + } + + /// Collect any assembled instructions that are ready to be output. + fn take(&mut self) -> Vec { + let output = self.concretize_ops(); + match output { + Ok(v) => { + self.ready.clear(); + v + } + Err(_) => Vec::new(), + } + } + + /// Concretize all assembled instructions. + fn concretize_ops(&mut self) -> Result, Error> { + let mut output = Vec::new(); + for op in self.ready.iter() { + if let RawOp::Op(ref op) = op { + match op + .clone() + .concretize((&self.declared_labels, &self.declared_macros).into()) + { + Ok(cop) => cop.assemble(&mut output), + Err(ops::Error::ContextIncomplete { + source: UnknownLabel { label: _label, .. }, + }) => { + let undeclared_names: Vec<_> = self + .undeclared_labels + .iter() + .map(|PendingLabel { label, .. }| label.clone()) + .collect(); + return error::UndeclaredLabels { + labels: undeclared_names, } - _ => unreachable!(), + .fail(); + } + Err(ops::Error::ContextIncomplete { + source: UnknownMacro { name, .. }, + }) => { + return error::UndeclaredInstructionMacro { name }.fail(); } + Err(ops::Error::ContextIncomplete { + source: UndefinedVariable { name, .. }, + }) => { + return error::UndeclaredVariableMacro { var: name }.fail(); + } + + Err(_) => unreachable!("all ops should be concretizable"), } - // bug: if a variable is used when it isn't available, e.g. push1 $size - _ => unreachable!(), - }; + } else if let RawOp::Raw(raw) = op { + output.extend(raw); + } } - if !self.ready.is_empty() { - panic!("not all assembled bytecode has been taken"); + Ok(output) + } + + /// Check if the input sequence is complete. Returns any errors that + /// may remain. + fn finish(&mut self) -> Result<(), Error> { + if !self.undeclared_labels.is_empty() { + return error::UndeclaredLabels { + labels: self + .undeclared_labels + .iter() + .map(|l| l.label.to_owned()) + .collect::>(), + } + .fail(); } Ok(()) } - /// Collect any assembled instructions that are ready to be output. - pub fn take(&mut self) -> Vec { - std::mem::take(&mut self.ready) - } - /// Feed instructions into the `Assembler`. /// - /// Returns the number of bytes that can be collected with [`Assembler::take`]. - pub fn push_all(&mut self, ops: I) -> Result + /// Returns the code of the assembled program. + pub fn assemble(&mut self, ops: I) -> Result, Error> where - I: IntoIterator, + I: IntoIterator + Clone, O: Into, { + self.inspect_macros(&ops)?; + for op in ops { self.push(op)?; } - Ok(self.ready.len()) + self.finish()?; + + Ok(self.take()) } - /// Insert explicilty declared macros and labels, via `AbstractOp`, and implictly declared - /// macros and labels via usage in `Op`. - fn declare_content(&mut self, rop: &RawOp) -> Result<(), Error> { - match rop { - RawOp::Op(AbstractOp::Label(ref label)) => { - match self.declared_labels.entry(label.to_owned()) { - hash_map::Entry::Occupied(_) => { - return error::DuplicateLabel { label }.fail(); - } - hash_map::Entry::Vacant(v) => { - v.insert(None); - self.undeclared_labels.remove(label); - } - } - } - RawOp::Op(AbstractOp::MacroDefinition(ref defn)) => { - match self.declared_macros.entry(defn.name().to_owned()) { - hash_map::Entry::Occupied(_) => { - return error::DuplicateMacro { name: defn.name() }.fail() - } - hash_map::Entry::Vacant(v) => { - v.insert(defn.to_owned()); - } + /// Insert explicitly declared macros, via `AbstractOp`, into the `Assembler`. + fn declare_macro(&mut self, rop: O) -> Result<(), Error> + where + O: Into, + { + let rop = rop.into(); + if let RawOp::Op(AbstractOp::MacroDefinition(ref defn)) = rop { + match self.declared_macros.entry(defn.name().to_owned()) { + hash_map::Entry::Occupied(_) => { + return error::DuplicateMacro { name: defn.name() }.fail() } - } - _ => (), - }; - - // Get all labels used by `rop`, check if they've been defined, and if not, note them as - // "undeclared". - if let Some(Ok(labels)) = rop.expr().map(|e| e.labels(&self.declared_macros)) { - for label in labels { - if !self.declared_labels.contains_key(&label) { - self.undeclared_labels.insert(label.to_owned()); + hash_map::Entry::Vacant(v) => { + v.insert(defn.to_owned()); } } } @@ -353,39 +360,59 @@ impl Assembler { } /// Feed a single instruction into the `Assembler`. - /// - /// Returns the number of bytes that can be collected with [`Assembler::take`] - pub fn push(&mut self, rop: O) -> Result + fn push(&mut self, rop: O) -> Result where O: Into, { let rop = rop.into(); - self.declare_content(&rop)?; + self.declare_label(&rop)?; - // Expand instruction macros immediately. We do this here because it's the same process - // regardless if we `push_read` or `push_pending` -- in fact, `expand_macro` pushes each op - // individually which calls the correct unchecked push. + // Expand instruction macros immediately. if let RawOp::Op(AbstractOp::Macro(ref m)) = rop { self.expand_macro(&m.name, &m.parameters)?; - return Ok(self.ready.len()); + return Ok(self.concrete_len); } - self.push_unchecked(rop)?; - Ok(self.ready.len()) + self.push_rawop(rop)?; + Ok(self.concrete_len) } - fn push_unchecked(&mut self, rop: RawOp) -> Result<(), Error> { - if self.pending.is_empty() && self.pending_len.is_some() { - self.push_ready(rop) - } else { - self.push_pending(rop) + fn declare_label(&mut self, rop: &RawOp) -> Result<(), Error> { + if let RawOp::Op(AbstractOp::Label(ref label)) = *rop { + match self.declared_labels.entry(label.to_owned()) { + hash_map::Entry::Occupied(_) => { + return error::DuplicateLabel { label }.fail(); + } + hash_map::Entry::Vacant(v) => { + v.insert(None); + } + } } + Ok(()) } - fn push_ready(&mut self, rop: RawOp) -> Result<(), Error> { + fn push_rawop(&mut self, rop: RawOp) -> Result<(), Error> { match rop { RawOp::Op(AbstractOp::Label(label)) => { + let mut dst = 0; + for ul in self.undeclared_labels.iter() { + if (ul.label == label) & ul.dynamic_push { + // Compensation in case label was dynamically pushed. + let mut tmp = + ((self.concrete_len as f32 - ul.position as f32) / 256.0).floor(); + + // Size already accounted for %push(label) was 2. + if tmp > 1.0 { + tmp -= 1.0; + } + dst = cmp::max(tmp as usize, dst); + } + } + + self.undeclared_labels.retain(|l| l.label != *label); + self.concrete_len += dst; + let old = self .declared_labels .insert(label, Some(self.concrete_len)) @@ -394,18 +421,7 @@ impl Assembler { Ok(()) } RawOp::Op(AbstractOp::MacroDefinition(_)) => Ok(()), - RawOp::Op(AbstractOp::Macro(ref m)) => { - match self.declared_macros.get(&m.name) { - // Do nothing if the instruction macro has been defined. - Some(MacroDefinition::Instruction(_)) => (), - _ => { - assert_eq!(self.pending_len, Some(0)); - self.pending_len = None; - self.pending.push_back(rop); - } - } - Ok(()) - } + RawOp::Op(AbstractOp::Macro(_)) => Ok(()), RawOp::Op(ref op) => { match op .clone() @@ -413,7 +429,7 @@ impl Assembler { { Ok(cop) => { self.concrete_len += cop.size(); - cop.assemble(&mut self.ready); + self.ready.push(rop) } Err(ops::Error::ExpressionTooLarge { value, spec, .. }) => { return error::ExpressionTooLarge { @@ -430,203 +446,48 @@ impl Assembler { } .fail() } - Err(_) => { - assert_eq!(self.pending_len, Some(0)); - self.pending_len = rop.size(); - self.pending.push_back(rop); + Err(ops::Error::ContextIncomplete { + source: UnknownLabel { label: _label, .. }, + }) => { + let mut dynamic_push = false; + match op.size() { + Some(size) => self.concrete_len += size, + None => { + self.concrete_len += 2; + dynamic_push = true; + } + }; + + self.undeclared_labels.push(PendingLabel { + label: _label.to_owned(), + position: self.ready.len(), + dynamic_push, + }); + self.ready.push(rop); } + Err(ops::Error::ContextIncomplete { + source: UnknownMacro { name, .. }, + }) => return error::UndeclaredInstructionMacro { name }.fail(), + Err(ops::Error::ContextIncomplete { + source: UndefinedVariable { name, .. }, + }) => return error::UndeclaredVariableMacro { var: name }.fail(), } Ok(()) } RawOp::Raw(raw) => { self.concrete_len += raw.len(); - self.ready.extend(raw); + self.ready.push(RawOp::Raw(raw)); Ok(()) } - } - } - - fn push_pending(&mut self, rop: RawOp) -> Result<(), Error> { - // Update total size of pending ops. - if let Some(ref mut pending_len) = self.pending_len { - match rop.size() { - Some(size) => *pending_len += size, - None => self.pending_len = None, - } - } - - // Handle new label and macro definitions. - match (self.pending_len, rop) { - (Some(pending_len), RawOp::Op(AbstractOp::Label(lbl))) => { - // The label has a defined address. - let address = self.concrete_len + pending_len; - let item = self.declared_labels.get_mut(&*lbl).unwrap(); - *item = Some(address); - } - (None, rop @ RawOp::Op(AbstractOp::Label(_))) => { - self.pending.push_back(rop); - if self.undeclared_labels.is_empty() { - self.choose_sizes()?; - } - } - (_, RawOp::Op(AbstractOp::MacroDefinition(defn))) => { - if let Some(RawOp::Op(AbstractOp::Macro(invc))) = self.pending.front() { - if defn.name() == &invc.name { - let invc = invc.clone(); - self.pending.pop_front(); - self.expand_macro(&invc.name, &invc.parameters)?; - } - } - } - (_, rop) => { - // Not a label. - self.pending.push_back(rop); - } - } - - // Repeatedly check if the front of the pending list is ready. - while let Some(next) = self.pending.front() { - let op = match next { - RawOp::Op(AbstractOp::Push(Imm { - tree: Expression::Terminal(Terminal::Label(_)), - .. - })) => { - if self.undeclared_labels.is_empty() { - unreachable!() - } else { - // Still waiting on more labels. - break; - } - } - RawOp::Op(AbstractOp::Label(_)) => unreachable!(), - RawOp::Op(AbstractOp::Macro(_)) => { - // Still waiting on more macros. - break; - } - RawOp::Op(op) => op, - RawOp::Raw(_) => { - self.pop_pending()?; - continue; - } - }; - - match op - .clone() - .concretize((&self.declared_labels, &self.declared_macros).into()) - { - Ok(cop) => { - let front = self.pending.front_mut().unwrap(); - *front = RawOp::Op(cop.into()); - } - Err(ops::Error::ExpressionTooLarge { value, spec, .. }) => { - return error::ExpressionTooLarge { - expr: op.expr().unwrap().clone(), - value, - spec, - } - .fail(); - } - Err(_) => { - // Still waiting for some definition. - break; - } - } - - self.pop_pending()?; - } - - Ok(()) - } - - fn pop_pending(&mut self) -> Result<(), Error> { - let popped = self.pending.pop_front().unwrap(); - - let size; - - match popped { - RawOp::Raw(raw) => { - size = raw.len(); - self.ready.extend(raw); - } - RawOp::Op(aop) => { - let cop = aop - .concretize((&self.declared_labels, &self.declared_macros).into()) - // Already able to concretize in `push_pending` loop. - .unwrap(); - size = cop.size(); - cop.assemble(&mut self.ready); - } - } - - self.concrete_len += size; - - if self.pending.is_empty() { - self.pending_len = Some(0); - } else if let Some(ref mut pending_len) = self.pending_len { - *pending_len -= size; - } - - Ok(()) - } - - fn choose_sizes(&mut self) -> Result<(), Error> { - let mut sizes: HashMap> = self - .pending - .iter() - .filter(|op| matches!(op, RawOp::Op(AbstractOp::Push(_)))) - .map(|op| (op.expr().unwrap().clone(), Op::<()>::push_for(1).unwrap())) - .collect(); - - let mut subasm; - - loop { - // Create a sub-assembler to try assembling with the sizes in - // `undefined_labels`. - subasm = Self::default(); - subasm.concrete_len = self.concrete_len; - subasm.declared_labels = self.declared_labels.clone(); - - let result: Result, Error> = self - .pending - .iter() - .map(|op| { - let new = match op { - RawOp::Op(AbstractOp::Push(Imm { tree, .. })) => { - let new = sizes[tree].with(tree.clone()).unwrap(); - let aop = AbstractOp::new(new); - RawOp::Op(aop) - } - op => op.clone(), - }; - - subasm.push_pending(new) - }) - .collect(); - - match result { - Ok(_) => { - assert!(subasm.pending.is_empty()); - break; - } - Err(Error::ExpressionTooLarge { expr, .. }) => { - // If an expression is too large for an op, increase the width of that op. - let item = sizes.get_mut(&expr).unwrap(); - let new_size = item.upsize().context(error::UnsizedPushTooLarge)?; - *item = new_size; - } - Err(e) => return Err(e), + RawOp::Scope(scope) => { + let mut asm = Self::new(); + let scope_result = asm.assemble(scope)?; + self.concrete_len += scope_result.len(); + self.ready.push(RawOp::Raw(scope_result)); + Ok(()) } } - - // Insert the results of the sub-assembler into self. - let raw = subasm.take(); - self.pending_len = Some(raw.len()); - self.pending.clear(); - self.pending.push_back(RawOp::Raw(raw)); - self.declared_labels = subasm.declared_labels; - - Ok(()) } fn expand_macro( @@ -686,19 +547,12 @@ impl Assembler { } } - Ok(Some(self.push_all(m.contents)?)) - } - _ => { - assert_eq!(self.pending_len, Some(0)); - self.pending_len = None; - self.pending.push_back(RawOp::Op(AbstractOp::Macro( - ops::InstructionMacroInvocation { - name: name.to_string(), - parameters: parameters.to_vec(), - }, - ))); - Ok(None) + for op in m.contents.iter() { + self.push(op.clone())?; + } + Ok(Some(self.concrete_len)) } + _ => error::UndeclaredInstructionMacro { name }.fail(), } } } @@ -718,20 +572,19 @@ mod tests { #[test] fn assemble_variable_push_const_while_pending() -> Result<(), Error> { let mut asm = Assembler::new(); - let sz = asm.push_all(vec![ + let result = asm.assemble(vec![ AbstractOp::Op(Push1(Imm::with_label("label1")).into()), AbstractOp::Push(Terminal::Number(0xaabb.into()).into()), AbstractOp::Label("label1".into()), ])?; - assert_eq!(5, sz); - assert_eq!(asm.take(), hex!("600561aabb")); + assert_eq!(result, hex!("600561aabb")); Ok(()) } #[test] fn assemble_variable_pushes_abab() -> Result<(), Error> { let mut asm = Assembler::new(); - let sz = asm.push_all(vec![ + let result = asm.assemble(vec![ AbstractOp::new(JumpDest), AbstractOp::Push(Imm::with_label("label1")), AbstractOp::Push(Imm::with_label("label2")), @@ -740,15 +593,14 @@ mod tests { AbstractOp::Label("label2".into()), AbstractOp::new(GetPc), ])?; - assert_eq!(7, sz); - assert_eq!(asm.take(), hex!("5b600560065858")); + assert_eq!(result, hex!("5b600560065858")); Ok(()) } #[test] fn assemble_variable_pushes_abba() -> Result<(), Error> { let mut asm = Assembler::new(); - let sz = asm.push_all(vec![ + let result = asm.assemble(vec![ AbstractOp::new(JumpDest), AbstractOp::Push(Imm::with_label("label1")), AbstractOp::Push(Imm::with_label("label2")), @@ -757,33 +609,30 @@ mod tests { AbstractOp::Label("label1".into()), AbstractOp::new(GetPc), ])?; - assert_eq!(7, sz); - assert_eq!(asm.take(), hex!("5b600660055858")); + assert_eq!(result, hex!("5b600660055858")); Ok(()) } #[test] fn assemble_variable_push1_multiple() -> Result<(), Error> { let mut asm = Assembler::new(); - let sz = asm.push_all(vec![ + let result = asm.assemble(vec![ AbstractOp::new(JumpDest), AbstractOp::Push(Imm::with_label("auto")), AbstractOp::Push(Imm::with_label("auto")), AbstractOp::Label("auto".into()), ])?; - assert_eq!(5, sz); - assert_eq!(asm.take(), hex!("5b60056005")); + assert_eq!(result, hex!("5b60056005")); Ok(()) } #[test] fn assemble_variable_push_const() -> Result<(), Error> { let mut asm = Assembler::new(); - let sz = asm.push_all(vec![AbstractOp::Push( + let result = asm.assemble(vec![AbstractOp::Push( Terminal::Number((0x00aaaaaaaaaaaaaaaaaaaaaaaa as u128).into()).into(), )])?; - assert_eq!(13, sz); - assert_eq!(asm.take(), hex!("6baaaaaaaaaaaaaaaaaaaaaaaa")); + assert_eq!(result, hex!("6baaaaaaaaaaaaaaaaaaaaaaaa")); Ok(()) } @@ -793,7 +642,7 @@ mod tests { let mut asm = Assembler::new(); let err = asm - .push_all(vec![AbstractOp::Push(Terminal::Number(v).into())]) + .assemble(vec![AbstractOp::Push(Terminal::Number(v).into())]) .unwrap_err(); assert_matches!(err, Error::ExpressionTooLarge { .. }); @@ -803,7 +652,7 @@ mod tests { fn assemble_variable_push_negative() { let mut asm = Assembler::new(); let err = asm - .push_all(vec![AbstractOp::Push(Terminal::Number((-1).into()).into())]) + .assemble(vec![AbstractOp::Push(Terminal::Number((-1).into()).into())]) .unwrap_err(); assert_matches!(err, Error::ExpressionNegative { .. }); @@ -812,73 +661,68 @@ mod tests { #[test] fn assemble_variable_push_const0() -> Result<(), Error> { let mut asm = Assembler::new(); - let sz = asm.push_all(vec![AbstractOp::Push( + let result = asm.assemble(vec![AbstractOp::Push( Terminal::Number((0x00 as u128).into()).into(), )])?; - assert_eq!(2, sz); - assert_eq!(asm.take(), hex!("6000")); + assert_eq!(result, hex!("6000")); Ok(()) } #[test] fn assemble_variable_push1_known() -> Result<(), Error> { let mut asm = Assembler::new(); - let sz = asm.push_all(vec![ + let result = asm.assemble(vec![ AbstractOp::new(JumpDest), AbstractOp::Label("auto".into()), AbstractOp::Push(Imm::with_label("auto")), ])?; - assert_eq!(3, sz); - assert_eq!(asm.take(), hex!("5b6001")); + assert_eq!(result, hex!("5b6001")); Ok(()) } #[test] fn assemble_variable_push1() -> Result<(), Error> { let mut asm = Assembler::new(); - let sz = asm.push_all(vec![ + let result = asm.assemble(vec![ AbstractOp::Push(Imm::with_label("auto")), AbstractOp::Label("auto".into()), AbstractOp::new(JumpDest), ])?; - assert_eq!(3, sz); - assert_eq!(asm.take(), hex!("60025b")); + assert_eq!(result, hex!("60025b")); Ok(()) } #[test] fn assemble_variable_push1_reuse() -> Result<(), Error> { let mut asm = Assembler::new(); - let sz = asm.push_all(vec![ + let result = asm.assemble(vec![ AbstractOp::Push(Imm::with_label("auto")), AbstractOp::Label("auto".into()), AbstractOp::new(JumpDest), AbstractOp::new(Push1(Imm::with_label("auto"))), ])?; - assert_eq!(5, sz); - assert_eq!(asm.take(), hex!("60025b6002")); + assert_eq!(result, hex!("60025b6002")); Ok(()) } #[test] fn assemble_variable_push2() -> Result<(), Error> { - let mut asm = Assembler::new(); - asm.push(AbstractOp::Push(Imm::with_label("auto")))?; + let mut code = vec![]; + code.push(AbstractOp::Push(Imm::with_label("auto"))); for _ in 0..255 { - asm.push(AbstractOp::new(GetPc))?; + code.push(AbstractOp::new(GetPc)); } - asm.push_all(vec![ - AbstractOp::Label("auto".into()), - AbstractOp::new(JumpDest), - ])?; + code.push(AbstractOp::Label("auto".into())); + code.push(AbstractOp::new(JumpDest)); + + let mut asm = Assembler::new(); + let result = asm.assemble(code)?; let mut expected = vec![0x61, 0x01, 0x02]; expected.extend_from_slice(&[0x58; 255]); expected.push(0x5b); - assert_eq!(asm.take(), expected); - - asm.finish()?; + assert_eq!(result, expected); Ok(()) } @@ -886,8 +730,9 @@ mod tests { #[test] fn assemble_undeclared_label() -> Result<(), Error> { let mut asm = Assembler::new(); - asm.push_all(vec![AbstractOp::new(Push1(Imm::with_label("hi")))])?; - let err = asm.finish().unwrap_err(); + let err = asm + .assemble(vec![AbstractOp::new(Push1(Imm::with_label("hi")))]) + .unwrap_err(); assert_matches!(err, Error::UndeclaredLabels { labels, .. } if labels == vec!["hi"]); Ok(()) } @@ -895,10 +740,9 @@ mod tests { #[test] fn assemble_jumpdest_no_label() -> Result<(), Error> { let mut asm = Assembler::new(); - let sz = asm.push_all(vec![AbstractOp::new(JumpDest)])?; - assert_eq!(1, sz); + let result = asm.assemble(vec![AbstractOp::new(JumpDest)])?; assert!(asm.declared_labels.is_empty()); - assert_eq!(asm.take(), hex!("5b")); + assert_eq!(result, hex!("5b")); Ok(()) } @@ -907,11 +751,10 @@ mod tests { let mut asm = Assembler::new(); let ops = vec![AbstractOp::Label("lbl".into()), AbstractOp::new(JumpDest)]; - let sz = asm.push_all(ops)?; - assert_eq!(1, sz); + let result = asm.assemble(ops)?; assert_eq!(asm.declared_labels.len(), 1); assert_eq!(asm.declared_labels.get("lbl"), Some(&Some(0))); - assert_eq!(asm.take(), hex!("5b")); + assert_eq!(result, hex!("5b")); Ok(()) } @@ -924,9 +767,8 @@ mod tests { ]; let mut asm = Assembler::new(); - let sz = asm.push_all(ops)?; - assert_eq!(sz, 3); - assert_eq!(asm.take(), hex!("5b6000")); + let result = asm.assemble(ops)?; + assert_eq!(result, hex!("5b6000")); Ok(()) } @@ -940,9 +782,8 @@ mod tests { ]; let mut asm = Assembler::new(); - let sz = asm.push_all(ops)?; - assert_eq!(sz, 3); - assert_eq!(asm.take(), hex!("600258")); + let result = asm.assemble(ops)?; + assert_eq!(result, hex!("600258")); Ok(()) } @@ -956,9 +797,8 @@ mod tests { ]; let mut asm = Assembler::new(); - let sz = asm.push_all(ops)?; - assert_eq!(sz, 3); - assert_eq!(asm.take(), hex!("60025b")); + let result = asm.assemble(ops)?; + assert_eq!(result, hex!("60025b")); Ok(()) } @@ -972,7 +812,7 @@ mod tests { ops.push(AbstractOp::new(JumpDest)); ops.push(AbstractOp::new(Push1(Imm::with_label("a")))); let mut asm = Assembler::new(); - let err = asm.push_all(ops).unwrap_err(); + let err = asm.assemble(ops).unwrap_err(); assert_matches!(err, Error::ExpressionTooLarge { expr: Expression::Terminal(Terminal::Label(label)), .. } if label == "a"); } @@ -985,11 +825,7 @@ mod tests { ops.push(AbstractOp::new(JumpDest)); ops.push(AbstractOp::new(Push1(Imm::with_label("b")))); let mut asm = Assembler::new(); - let sz = asm.push_all(ops)?; - assert_eq!(sz, 259); - - let assembled = asm.take(); - asm.finish()?; + let result = asm.assemble(ops)?; let mut expected = vec![0x58; 255]; expected.push(0x5b); @@ -997,7 +833,7 @@ mod tests { expected.push(0x60); expected.push(0xff); - assert_eq!(assembled, expected); + assert_eq!(result, expected); Ok(()) } @@ -1028,10 +864,8 @@ mod tests { ]; let mut asm = Assembler::new(); - let sz = asm.push_all(ops)?; - assert_eq!(sz, 0); - let out = asm.take(); - assert_eq!(out, []); + let result = asm.assemble(ops)?; + assert_eq!(result, []); Ok(()) } @@ -1064,10 +898,8 @@ mod tests { ]; let mut asm = Assembler::new(); - let sz = asm.push_all(ops)?; - assert_eq!(sz, 13); - let out = asm.take(); - assert_eq!(out, hex!("5b60005b600360005b60086000")); + let result = asm.assemble(ops)?; + assert_eq!(result, hex!("5b60005b600360005b60086000")); Ok(()) } @@ -1096,10 +928,8 @@ mod tests { ]; let mut asm = Assembler::new(); - let sz = asm.push_all(ops)?; - assert_eq!(sz, 8); - let out = asm.take(); - assert_eq!(out, hex!("5b60005b60036000")); + let result = asm.assemble(ops)?; + assert_eq!(result, hex!("5b60005b60036000")); Ok(()) } @@ -1128,10 +958,8 @@ mod tests { ]; let mut asm = Assembler::new(); - let sz = asm.push_all(ops)?; - assert_eq!(sz, 8); - let out = asm.take(); - assert_eq!(out, hex!("5b60005b60036000")); + let result = asm.assemble(ops)?; + assert_eq!(result, hex!("5b60005b60036000")); Ok(()) } @@ -1160,9 +988,8 @@ mod tests { ]; let mut asm = Assembler::new(); - let sz = asm.push_all(ops)?; - assert_eq!(7, sz); - assert_eq!(asm.take(), hex!("5b600560065858")); + let result = asm.assemble(ops)?; + assert_eq!(result, hex!("5b600560065858")); Ok(()) } @@ -1173,8 +1000,7 @@ mod tests { InstructionMacroInvocation::with_zero_parameters("my_macro".into()), )]; let mut asm = Assembler::new(); - asm.push_all(ops)?; - let err = asm.finish().unwrap_err(); + let err = asm.assemble(ops).unwrap_err(); assert_matches!(err, Error::UndeclaredInstructionMacro { name, .. } if name == "my_macro"); Ok(()) @@ -1197,7 +1023,7 @@ mod tests { .into(), ]; let mut asm = Assembler::new(); - let err = asm.push_all(ops).unwrap_err(); + let err = asm.assemble(ops).unwrap_err(); assert_matches!(err, Error::DuplicateMacro { name, .. } if name == "my_macro"); Ok(()) @@ -1217,7 +1043,7 @@ mod tests { )), ]; let mut asm = Assembler::new(); - let err = asm.push_all(ops).unwrap_err(); + let err = asm.assemble(ops).unwrap_err(); assert_matches!(err, Error::DuplicateLabel { label, .. } if label == "a"); Ok(()) @@ -1244,13 +1070,9 @@ mod tests { AbstractOp::new(Push1(Imm::with_label("a"))), ]; let mut asm = Assembler::new(); - let sz = asm.push_all(ops)?; - assert_eq!(sz, 5); - - let out = asm.take(); - asm.finish()?; + let result = asm.assemble(ops)?; - assert_eq!(out, hex!("3360016000")); + assert_eq!(result, hex!("3360016000")); Ok(()) } @@ -1280,10 +1102,8 @@ mod tests { ]; let mut asm = Assembler::new(); - let sz = asm.push_all(ops)?; - assert_eq!(sz, 7); - let out = asm.take(); - assert_eq!(out, hex!("5b600060426000")); + let result = asm.assemble(ops)?; + assert_eq!(result, hex!("5b600060426000")); Ok(()) } @@ -1295,10 +1115,8 @@ mod tests { )))]; let mut asm = Assembler::new(); - let sz = asm.push_all(ops)?; - assert_eq!(sz, 2); - let out = asm.take(); - assert_eq!(out, hex!("6002")); + let result = asm.assemble(ops)?; + assert_eq!(result, hex!("6002")); Ok(()) } @@ -1309,7 +1127,7 @@ mod tests { BigInt::from(-1).into(), )))]; let mut asm = Assembler::new(); - let err = asm.push_all(ops).unwrap_err(); + let err = asm.assemble(ops).unwrap_err(); assert_matches!(err, Error::ExpressionNegative { value, .. } if value == BigInt::from(-1)); Ok(()) @@ -1318,10 +1136,11 @@ mod tests { #[test] fn assemble_expression_undeclared_label() -> Result<(), Error> { let mut asm = Assembler::new(); - asm.push_all(vec![AbstractOp::new(Push1(Imm::with_expression( - Terminal::Label(String::from("hi")).into(), - )))])?; - let err = asm.finish().unwrap_err(); + let err = asm + .assemble(vec![AbstractOp::new(Push1(Imm::with_expression( + Terminal::Label(String::from("hi")).into(), + )))]) + .unwrap_err(); assert_matches!(err, Error::UndeclaredLabels { labels, .. } if labels == vec!["hi"]); Ok(()) } @@ -1329,23 +1148,25 @@ mod tests { #[test] fn assemble_variable_push_expression_with_undeclared_labels() -> Result<(), Error> { let mut asm = Assembler::new(); - asm.push_all(vec![ - AbstractOp::new(JumpDest), - AbstractOp::Push(Imm::with_expression(Expression::Plus( - Terminal::Label("foo".into()).into(), - Terminal::Label("bar".into()).into(), - ))), - AbstractOp::new(Gas), - ])?; - let err = asm.finish().unwrap_err(); - assert_matches!(err, Error::UndeclaredLabels { labels, .. } if (labels.contains(&"foo".to_string())) && labels.contains(&"bar".to_string())); + let err = asm + .assemble(vec![ + AbstractOp::new(JumpDest), + AbstractOp::Push(Imm::with_expression(Expression::Plus( + Terminal::Label("foo".into()).into(), + Terminal::Label("bar".into()).into(), + ))), + AbstractOp::new(Gas), + ]) + .unwrap_err(); + // The expressions have short-circuit evaluation, so only the first label is caught in the error. + assert_matches!(err, Error::UndeclaredLabels { labels, .. } if (labels.contains(&"foo".to_string()))); Ok(()) } #[test] fn assemble_variable_push1_expression() -> Result<(), Error> { let mut asm = Assembler::new(); - let sz = asm.push_all(vec![ + let result = asm.assemble(vec![ AbstractOp::new(JumpDest), AbstractOp::Label("auto".into()), AbstractOp::Push(Imm::with_expression(Expression::Plus( @@ -1353,15 +1174,14 @@ mod tests { Terminal::Label(String::from("auto")).into(), ))), ])?; - assert_eq!(3, sz); - assert_eq!(asm.take(), hex!("5b6002")); + assert_eq!(result, hex!("5b6002")); Ok(()) } #[test] fn assemble_expression_with_labels() -> Result<(), Error> { let mut asm = Assembler::new(); - let sz = asm.push_all(vec![ + let result = asm.assemble(vec![ AbstractOp::new(JumpDest), AbstractOp::Push(Imm::with_expression(Expression::Plus( Terminal::Label(String::from("foo")).into(), @@ -1371,8 +1191,7 @@ mod tests { AbstractOp::Label("foo".into()), AbstractOp::Label("bar".into()), ])?; - assert_eq!(4, sz); - assert_eq!(asm.take(), hex!("5b60085a")); + assert_eq!(result, hex!("5b60085a")); Ok(()) } @@ -1392,10 +1211,98 @@ mod tests { ]; let mut asm = Assembler::new(); - let sz = asm.push_all(ops)?; - assert_eq!(sz, 2); - let out = asm.take(); - assert_eq!(out, hex!("6002")); + let result = asm.assemble(ops)?; + assert_eq!(result, hex!("6002")); + + Ok(()) + } + + #[test] + fn assemble_instruction_macro_with_undeclared_variables() { + let ops = vec![ + InstructionMacroDefinition { + name: "my_macro".into(), + parameters: vec!["foo".into()], + contents: vec![AbstractOp::new(Push1(Imm::with_variable("bar")))], + } + .into(), + AbstractOp::Label("b".into()), + AbstractOp::new(JumpDest), + AbstractOp::new(Push1(Imm::with_label("b"))), + AbstractOp::Macro(InstructionMacroInvocation { + name: "my_macro".into(), + parameters: vec![BigInt::from_bytes_be(Sign::Plus, &vec![0x42]).into()], + }), + ]; + + let mut asm = Assembler::new(); + let err = asm.assemble(ops).unwrap_err(); + + assert_matches!(err, Error::UndeclaredVariableMacro { var, .. } if var == "bar"); + } + + #[test] + fn assemble_instruction_macro_two_delayed_definitions_mirrored() -> Result<(), Error> { + let ops = vec![ + AbstractOp::new(GetPc), + AbstractOp::Macro(InstructionMacroInvocation { + name: "macro1".into(), + parameters: vec![], + }), + AbstractOp::Macro(InstructionMacroInvocation { + name: "macro0".into(), + parameters: vec![], + }), + InstructionMacroDefinition { + name: "macro0".into(), + parameters: vec![], + contents: vec![AbstractOp::new(JumpDest)], + } + .into(), + InstructionMacroDefinition { + name: "macro1".into(), + parameters: vec![], + contents: vec![AbstractOp::new(Caller)], + } + .into(), + ]; + + let mut asm = Assembler::new(); + let result = asm.assemble(ops)?; + assert_eq!(result, hex!("58335b")); + + Ok(()) + } + + #[test] + fn assemble_instruction_macro_two_delayed_definitions() -> Result<(), Error> { + let ops = vec![ + AbstractOp::new(GetPc), + AbstractOp::Macro(InstructionMacroInvocation { + name: "macro0".into(), + parameters: vec![], + }), + AbstractOp::Macro(InstructionMacroInvocation { + name: "macro1".into(), + parameters: vec![], + }), + InstructionMacroDefinition { + name: "macro0".into(), + parameters: vec![], + contents: vec![AbstractOp::new(JumpDest)], + } + .into(), + InstructionMacroDefinition { + name: "macro1".into(), + parameters: vec![], + contents: vec![AbstractOp::new(Caller)], + } + .into(), + ]; + + let mut asm = Assembler::new(); + let result = asm.assemble(ops)?; + assert_eq!(result, hex!("585b33")); Ok(()) } diff --git a/etk-asm/src/ast.rs b/etk-asm/src/ast.rs index a4d2e15d..e3824c15 100644 --- a/etk-asm/src/ast.rs +++ b/etk-asm/src/ast.rs @@ -1,16 +1,15 @@ +use std::path::PathBuf; + use crate::ops::{Abstract, AbstractOp, ExpressionMacroDefinition, InstructionMacroDefinition}; use etk_ops::cancun::Op; -use std::path::PathBuf; #[derive(Debug, Clone, PartialEq)] pub(crate) enum Node { Op(AbstractOp), - Raw(Vec), Import(PathBuf), Include(PathBuf), IncludeHex(PathBuf), } - impl From> for Node { fn from(op: Op) -> Self { Node::Op(AbstractOp::Op(op)) diff --git a/etk-asm/src/ingest.rs b/etk-asm/src/ingest.rs index 05227609..f6ba6247 100644 --- a/etk-asm/src/ingest.rs +++ b/etk-asm/src/ingest.rs @@ -51,13 +51,15 @@ mod error { }, /// An error that occurred while parsing a file. - #[snafu(context(false))] #[non_exhaustive] - #[snafu(display("parsing failed"))] + #[snafu(display("parsing failed on path `{}`", path.to_string_lossy()))] Parse { /// The underlying source of this error. #[snafu(backtrace)] source: ParseError, + + /// The location of the error. + path: PathBuf, }, /// An error that occurred while assembling a file. @@ -106,40 +108,7 @@ use std::fs::{read_to_string, File}; use std::io::{self, Read, Write}; use std::path::{Path, PathBuf}; -fn parse_file>(path: P) -> Result, Error> { - let asm = read_to_string(path.as_ref()).with_context(|_| error::Io { - message: "reading file before parsing", - path: path.as_ref().to_owned(), - })?; - let nodes = parse_asm(&asm)?; - - Ok(nodes) -} - -#[derive(Debug)] -enum Scope { - Same, - Independent(Box), -} - -impl Scope { - fn same() -> Self { - Self::Same - } - - fn independent() -> Self { - Self::Independent(Box::new(Assembler::new())) - } -} - -#[derive(Debug)] -struct Source { - path: PathBuf, - nodes: std::vec::IntoIter, - scope: Scope, -} - -#[derive(Debug)] +#[derive(Debug, Clone)] struct Root { original: PathBuf, canonicalized: PathBuf, @@ -211,135 +180,44 @@ impl Root { } } -#[must_use] -struct PartialSource<'a, W> { - stack: &'a mut SourceStack, - path: PathBuf, - scope: Scope, -} - -impl<'a, W> PartialSource<'a, W> { - fn path(&self) -> &Path { - &self.path - } - - fn push(self, nodes: Vec) -> &'a mut Source { - self.stack.sources.push(Source { - path: self.path, - nodes: nodes.into_iter(), - scope: self.scope, - }); - - self.stack.sources.last_mut().unwrap() - } -} - #[derive(Debug)] -struct SourceStack { - output: W, - sources: Vec, +struct Program { root: Option, + sources: Vec, } -impl SourceStack { - fn new(output: W) -> Self { +impl Program { + fn new(path: PathBuf) -> Self { Self { - output, - sources: Default::default(), - root: Default::default(), + root: Root::new(path.clone()).ok(), + sources: vec![path], } } - fn resolve(&mut self, path: PathBuf, scope: Scope) -> Result, Error> { + fn push_path(&mut self, path: &PathBuf) -> Result { ensure!(self.sources.len() <= 255, error::RecursionLimit); let path = if let Some(ref root) = self.root { let last = self.sources.last().unwrap(); - let dir = match last.path.parent() { + let dir = match last.parent() { Some(s) => s, None => Path::new("./"), }; let candidate = dir.join(path); root.check(&candidate)?; + self.sources.push(candidate.clone()); candidate } else { assert!(self.sources.is_empty()); - self.root = Some(Root::new(path.clone())?); - path + self.root = Some(Root::new(path.to_owned())?); + path.clone() }; - Ok(PartialSource { - stack: self, - path, - scope, - }) + Ok(path) } - fn peek(&mut self) -> Option<&mut Source> { - self.sources.last_mut() - } -} - -impl SourceStack -where - W: Write, -{ - fn pop(&mut self) -> Result<(), Error> { - let popped = self.sources.pop().unwrap(); - - if self.sources.is_empty() { - self.root = None; - } - - let mut asm = match popped.scope { - Scope::Independent(a) => a, - Scope::Same => return Ok(()), - }; - - let raw = asm.take(); - asm.finish()?; - - if raw.is_empty() { - return Ok(()); - } - - if self.sources.is_empty() { - self.output.write_all(&raw).context(error::Io { - message: "writing output", - path: None, - })?; - Ok(()) - } else { - self.write(RawOp::Raw(raw)) - } - } - - fn write(&mut self, mut op: RawOp) -> Result<(), Error> { - if self.sources.is_empty() { - panic!("no sources!"); - } - - for frame in self.sources[1..].iter_mut().rev() { - let asm = match frame.scope { - Scope::Same => continue, - Scope::Independent(ref mut a) => a, - }; - - if 0 == asm.push(op)? { - return Ok(()); - } else { - op = RawOp::Raw(asm.take()); - } - } - - let first_asm = match self.sources[0].scope { - Scope::Independent(ref mut a) => a, - Scope::Same => panic!("sources[0] must be independent"), - }; - - first_asm.push(op)?; - - Ok(()) + fn pop_path(&mut self) { + self.sources.pop(); } } @@ -370,15 +248,13 @@ where /// ``` #[derive(Debug)] pub struct Ingest { - sources: SourceStack, + output: W, } impl Ingest { /// Make a new `Ingest` that writes assembled bytes to `output`. pub fn new(output: W) -> Self { - Self { - sources: SourceStack::new(output), - } + Self { output } } } @@ -403,7 +279,8 @@ where path: path.clone(), })?; - self.ingest(path, &text) + self.ingest(path, &text)?; + Ok(()) } /// Assemble instructions from `src` as if they were read from a file located @@ -412,61 +289,70 @@ where where P: Into, { - let nodes = parse_asm(src)?; - let partial = self.sources.resolve(path.into(), Scope::independent())?; - partial.push(nodes); - - while let Some(source) = self.sources.peek() { - let node = match source.nodes.next() { - Some(n) => n, - None => { - self.sources.pop()?; - continue; - } - }; + let mut program = Program::new(path.into()); + let nodes = self.preprocess(&mut program, src)?; + let mut asm = Assembler::new(); + let raw = asm.assemble(nodes)?; + + self.output.write_all(&raw).context(error::Io { + message: "writing output", + path: None, + })?; + + Ok(()) + } + fn preprocess(&mut self, program: &mut Program, src: &str) -> Result, Error> { + let nodes = parse_asm(src).with_context(|_| error::Parse { + path: program.sources.last().unwrap().clone(), + })?; + let mut raws = Vec::new(); + for node in nodes { match node { Node::Op(op) => { - self.sources.write(RawOp::Op(op))?; - } - Node::Raw(raw) => { - self.sources.write(RawOp::Raw(raw))?; + raws.push(RawOp::Op(op)); } - Node::Import(path) => { - let partial = self.sources.resolve(path, Scope::same())?; - let parsed = parse_file(partial.path())?; - partial.push(parsed); + Node::Import(imp_path) => { + let new_raws = self.resolve_and_ingest(program, imp_path)?; + raws.extend(new_raws); } - Node::Include(path) => { - let partial = self.sources.resolve(path, Scope::independent())?; - let parsed = parse_file(partial.path())?; - partial.push(parsed); + Node::Include(inc_path) => { + let inc_raws = self.resolve_and_ingest(program, inc_path)?; + raws.push(RawOp::Scope(inc_raws)); } - Node::IncludeHex(path) => { - let partial = self.sources.resolve(path, Scope::same())?; - - let file = - std::fs::read_to_string(partial.path()).with_context(|_| error::Io { - message: "reading hex include", - path: partial.path().to_owned(), - })?; + Node::IncludeHex(hex_path) => { + let file = std::fs::read_to_string(&hex_path).with_context(|_| error::Io { + message: "reading hex include", + path: hex_path.to_owned(), + })?; let raw = hex::decode(file.trim()) .map_err(|e| Box::new(e) as Box) .context(error::InvalidHex { - path: partial.path().to_owned(), + path: hex_path.to_owned(), })?; - partial.push(vec![Node::Raw(raw)]); + raws.push(RawOp::Raw(raw)) } } } - if !self.sources.sources.is_empty() { - panic!("extra sources?"); - } + Ok(raws) + } - Ok(()) + fn resolve_and_ingest( + &mut self, + program: &mut Program, + path: PathBuf, + ) -> Result, Error> { + let source = program.push_path(&path)?; + let code = read_to_string(source).with_context(|_| error::Io { + message: "reading file before parsing", + path: path.to_owned(), + })?; + let new_raws = self.preprocess(program, &code)?; + program.pop_path(); + Ok(new_raws) } } @@ -538,6 +424,7 @@ mod tests { let mut output = Vec::new(); let mut ingest = Ingest::new(&mut output); ingest.ingest(root, &text)?; + assert_eq!(output, hex!("60015b586000566002")); Ok(()) diff --git a/etk-asm/src/parse/mod.rs b/etk-asm/src/parse/mod.rs index dba643c7..4858abaa 100644 --- a/etk-asm/src/parse/mod.rs +++ b/etk-asm/src/parse/mod.rs @@ -19,6 +19,7 @@ use self::{ error::ParseError, parser::{AsmParser, Rule}, }; + use crate::ast::Node; use crate::ops::AbstractOp; use etk_ops::cancun::Op; diff --git a/etk-asm/tests/asm.rs b/etk-asm/tests/asm.rs index 36fc72cf..c09dba9f 100644 --- a/etk-asm/tests/asm.rs +++ b/etk-asm/tests/asm.rs @@ -108,6 +108,22 @@ fn instruction_macro_with_two_instructions_per_line() { assert_matches!(err, Error::Parse { .. }); } +#[test] +fn undefined_label_undefined_macro() { + let mut output = Vec::new(); + let mut ingester = Ingest::new(&mut output); + let err = ingester + .ingest_file(source(&[ + "instruction-macro", + "undefined-label-undefined-macro.etk", + ])) + .unwrap_err(); + + assert_matches!(err, etk_asm::ingest::Error::Assemble { source: + etk_asm::asm::Error::UndeclaredInstructionMacro { name, .. }, .. + } if name == "revert".to_string()); +} + #[test] fn every_op() -> Result<(), Error> { let mut output = Vec::new(); @@ -276,3 +292,14 @@ fn every_op() -> Result<(), Error> { Ok(()) } + +#[test] +fn test_dynamic_push_and_include() -> Result<(), Error> { + let mut output = Vec::new(); + let mut ingester = Ingest::new(&mut output); + ingester.ingest_file(source(&["variable-push", "main.etk"]))?; + + assert_eq!(output, hex!("61025758585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585801010101010158585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858015b58")); + + Ok(()) +} diff --git a/etk-asm/tests/asm/every-op/test.etk b/etk-asm/tests/asm/every-op/test.etk new file mode 100644 index 00000000..e1df3285 --- /dev/null +++ b/etk-asm/tests/asm/every-op/test.etk @@ -0,0 +1,5 @@ +%push(hello) +jump + +hello: +jumpdest \ No newline at end of file diff --git a/etk-asm/tests/asm/instruction-macro/undefined-label-undefined-macro.etk b/etk-asm/tests/asm/instruction-macro/undefined-label-undefined-macro.etk new file mode 100644 index 00000000..4103fb6b --- /dev/null +++ b/etk-asm/tests/asm/instruction-macro/undefined-label-undefined-macro.etk @@ -0,0 +1,6 @@ +%macro revert_if_neq() + push1 revert +%end + +%revert_if_neq() +%revert() \ No newline at end of file diff --git a/etk-asm/tests/asm/variable-push/included.etk b/etk-asm/tests/asm/variable-push/included.etk new file mode 100644 index 00000000..c55517ab --- /dev/null +++ b/etk-asm/tests/asm/variable-push/included.etk @@ -0,0 +1,594 @@ +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +add +add +add +add +add +add +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +pc +add diff --git a/etk-asm/tests/asm/variable-push/main.etk b/etk-asm/tests/asm/variable-push/main.etk new file mode 100644 index 00000000..cef9faf7 --- /dev/null +++ b/etk-asm/tests/asm/variable-push/main.etk @@ -0,0 +1,7 @@ +%push(label) +pc +pc +%include("included.etk") +label: +jumpdest +pc \ No newline at end of file diff --git a/etk-dasm/src/bin/disease.rs b/etk-dasm/src/bin/disease.rs index 7729d9e0..08afb442 100644 --- a/etk-dasm/src/bin/disease.rs +++ b/etk-dasm/src/bin/disease.rs @@ -55,10 +55,7 @@ fn run() -> Result<(), Error> { separator.push_all(disasm.ops()); - let basic_blocks = separator - .take() - .into_iter() - .chain(separator.finish().into_iter()); + let basic_blocks = separator.take().into_iter().chain(separator.finish()); for block in basic_blocks { let mut offset = block.offset; From b872b505cca21f2b985a02b6ff44ce06026e13f6 Mon Sep 17 00:00:00 2001 From: Gaston Zanitti Date: Fri, 27 Oct 2023 14:42:06 -0300 Subject: [PATCH 49/73] Rustfmt: Into to From --- etk-ops/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/etk-ops/src/lib.rs b/etk-ops/src/lib.rs index f25298b2..beadc6fb 100644 --- a/etk-ops/src/lib.rs +++ b/etk-ops/src/lib.rs @@ -485,9 +485,9 @@ impl Display for HardForkDirective { } } -impl Into for Option<&HardForkDirective> { - fn into(self) -> HardForkDirective { - match self { +impl From> for HardForkDirective { + fn from(option: Option<&HardForkDirective>) -> Self { + match option { Some(hfd) => hfd.to_owned(), None => panic!("Cannot convert None into HardForkDirective"), } From 73c30600fa15cf5b004c598f5297c2b6a595a721 Mon Sep 17 00:00:00 2001 From: Gaston Zanitti Date: Fri, 27 Oct 2023 16:00:09 -0300 Subject: [PATCH 50/73] Unused vars removed --- etk-ops/build.rs | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/etk-ops/build.rs b/etk-ops/build.rs index fdadc92b..d658df7f 100644 --- a/etk-ops/build.rs +++ b/etk-ops/build.rs @@ -309,8 +309,6 @@ fn generate_fork(fork_name: &str) -> Result<(), Error> { }); } - let mut debug_bound = quote! {}; - let mut clone_bound = quote! {}; let mut partial_eq_bound = quote! {}; let mut eq_bound = quote! {}; let mut ord_bound = quote! {}; @@ -321,14 +319,6 @@ fn generate_fork(fork_name: &str) -> Result<(), Error> { for ii in 1..=32usize { let ident = format_ident!("P{}", ii); - debug_bound.extend(quote! { - T::#ident: std::fmt::Debug, - }); - - clone_bound.extend(quote! { - T::#ident: Clone, - }); - partial_eq_bound.extend(quote! { T::#ident: std::cmp::PartialEq, }); @@ -352,8 +342,6 @@ fn generate_fork(fork_name: &str) -> Result<(), Error> { bounds.push(quote! { #ident }); } - //let debug_bound = debug_bound.to_string(); - //let clone_bound = clone_bound.to_string(); let partial_eq_bound = partial_eq_bound.to_string(); let eq_bound = eq_bound.to_string(); let ord_bound = ord_bound.to_string(); @@ -366,7 +354,6 @@ fn generate_fork(fork_name: &str) -> Result<(), Error> { #[educe( PartialEq(bound = #partial_eq_bound), Eq(bound = #eq_bound), - //Clone(bound = #clone_bound), Ord(bound = #ord_bound), PartialOrd(bound = #partial_ord_bound), Hash(bound = #hash_bound), From dbb2bc8840dcba28cee64368f0105345f431349a Mon Sep 17 00:00:00 2001 From: Gaston Zanitti Date: Fri, 27 Oct 2023 16:51:11 -0300 Subject: [PATCH 51/73] More tests. Better ranges --- etk-asm/src/ingest.rs | 2 +- etk-asm/src/parse/macros.rs | 23 +++++++++++++------ etk-asm/tests/asm/hardfork/valid-hardfork.etk | 6 ++++- 3 files changed, 22 insertions(+), 9 deletions(-) diff --git a/etk-asm/src/ingest.rs b/etk-asm/src/ingest.rs index 94b5b189..8e3b33e8 100644 --- a/etk-asm/src/ingest.rs +++ b/etk-asm/src/ingest.rs @@ -363,7 +363,7 @@ where raws.push(RawOp::Raw(raw)) } Node::HardforkMacro(directive) => { - // Here, directive is always a valid range. + // Here, `directive`` is always a valid range. let (hfd1, ophfd2) = directive; ensure!( self.hardfork.is_valid(&hfd1), diff --git a/etk-asm/src/parse/macros.rs b/etk-asm/src/parse/macros.rs index 58fd75eb..abcfbe40 100644 --- a/etk-asm/src/parse/macros.rs +++ b/etk-asm/src/parse/macros.rs @@ -51,13 +51,22 @@ pub(crate) fn parse_builtin(pair: Pair) -> Result { let mut directives = Vec::new(); for inner in pair.into_inner() { let mut directive = inner.into_inner(); - let operator = match directive.next() { - Some(operator) => { - let op = operator.as_str().into(); - Some(op) - } - None => None, - }; + let operator = + match directive + .peek() + .and_then(|operator| match operator.as_rule() { + Rule::lt => Some(OperatorDirective::LessThan), + Rule::lte => Some(OperatorDirective::LessThanOrEqual), + Rule::gt => Some(OperatorDirective::GreaterThan), + Rule::gte => Some(OperatorDirective::GreaterThanOrEqual), + _ => None, + }) { + Some(op) => { + directive.next(); + Some(op) + } + None => None, + }; // Tried moving this to into() but can't manage invalid hardforks. let hardforkstr = directive.next().unwrap().as_str(); diff --git a/etk-asm/tests/asm/hardfork/valid-hardfork.etk b/etk-asm/tests/asm/hardfork/valid-hardfork.etk index b69ae5e9..5050de8c 100644 --- a/etk-asm/tests/asm/hardfork/valid-hardfork.etk +++ b/etk-asm/tests/asm/hardfork/valid-hardfork.etk @@ -1 +1,5 @@ -%hardfork(">london,<=cancun") \ No newline at end of file +%hardfork(">london,<=cancun") +%hardfork(">=cancun,<=cancun") +%hardfork("cancun") +%hardfork(">london") +%hardfork("<=cancun") From eb366966b2ec17acdb4ba0402e6f638c61053f5a Mon Sep 17 00:00:00 2001 From: Gaston Zanitti Date: Mon, 30 Oct 2023 16:39:10 -0300 Subject: [PATCH 52/73] addressing comments --- etk-asm/src/asm.rs | 255 ++++++++++++++++++++---------------------- etk-asm/src/ingest.rs | 2 +- 2 files changed, 122 insertions(+), 135 deletions(-) diff --git a/etk-asm/src/asm.rs b/etk-asm/src/asm.rs index 3e5556db..07271003 100644 --- a/etk-asm/src/asm.rs +++ b/etk-asm/src/asm.rs @@ -174,6 +174,12 @@ impl From> for RawOp { } } +impl> From<&O> for RawOp { + fn from(item: &O) -> Self { + item.clone().into() + } +} + /// Assembles a series of [`RawOp`] into raw bytes, tracking and resolving macros and labels, /// and handling dynamic pushes. /// @@ -187,9 +193,8 @@ impl From> for RawOp { /// # /// # use hex_literal::hex; /// let mut asm = Assembler::new(); -/// let result = asm.assemble(vec![ -/// AbstractOp::new(GetPc), -/// ])?; +/// let code = ec![AbstractOp::new(GetPc)] +/// let result = asm.assemble(&code)?; /// # assert_eq!(result, hex!("58")); /// # Result::<(), Error>::Ok(()) /// ``` @@ -232,12 +237,11 @@ impl Assembler { } /// Inspect the macros in a series of [`RawOp`] and declare them in the assembler. - fn inspect_macros(&mut self, nodes: &I) -> Result<(), Error> + fn inspect_macros(&mut self, nodes: &[O]) -> Result<(), Error> where - I: IntoIterator + Clone, O: Into, { - for op in nodes.clone() { + for op in nodes { let op = op.into(); if let RawOp::Op(AbstractOp::MacroDefinition(_)) = op { self.declare_macro(op)? @@ -247,18 +251,6 @@ impl Assembler { Ok(()) } - /// Collect any assembled instructions that are ready to be output. - fn take(&mut self) -> Vec { - let output = self.concretize_ops(); - match output { - Ok(v) => { - self.ready.clear(); - v - } - Err(_) => Vec::new(), - } - } - /// Concretize all assembled instructions. fn concretize_ops(&mut self) -> Result, Error> { let mut output = Vec::new(); @@ -270,7 +262,7 @@ impl Assembler { { Ok(cop) => cop.assemble(&mut output), Err(ops::Error::ContextIncomplete { - source: UnknownLabel { label: _label, .. }, + source: UnknownLabel { .. }, }) => { let undeclared_names: Vec<_> = self .undeclared_labels @@ -323,9 +315,8 @@ impl Assembler { /// Feed instructions into the `Assembler`. /// /// Returns the code of the assembled program. - pub fn assemble(&mut self, ops: I) -> Result, Error> + pub fn assemble(&mut self, ops: &[O]) -> Result, Error> where - I: IntoIterator + Clone, O: Into, { self.inspect_macros(&ops)?; @@ -336,7 +327,9 @@ impl Assembler { self.finish()?; - Ok(self.take()) + let output = self.concretize_ops()?; + self.ready.clear(); + Ok(output) } /// Insert explicitly declared macros, via `AbstractOp`, into the `Assembler`. @@ -360,7 +353,7 @@ impl Assembler { } /// Feed a single instruction into the `Assembler`. - fn push(&mut self, rop: O) -> Result + fn push(&mut self, rop: &O) -> Result where O: Into, { @@ -368,31 +361,6 @@ impl Assembler { self.declare_label(&rop)?; - // Expand instruction macros immediately. - if let RawOp::Op(AbstractOp::Macro(ref m)) = rop { - self.expand_macro(&m.name, &m.parameters)?; - return Ok(self.concrete_len); - } - - self.push_rawop(rop)?; - Ok(self.concrete_len) - } - - fn declare_label(&mut self, rop: &RawOp) -> Result<(), Error> { - if let RawOp::Op(AbstractOp::Label(ref label)) = *rop { - match self.declared_labels.entry(label.to_owned()) { - hash_map::Entry::Occupied(_) => { - return error::DuplicateLabel { label }.fail(); - } - hash_map::Entry::Vacant(v) => { - v.insert(None); - } - } - } - Ok(()) - } - - fn push_rawop(&mut self, rop: RawOp) -> Result<(), Error> { match rop { RawOp::Op(AbstractOp::Label(label)) => { let mut dst = 0; @@ -418,10 +386,11 @@ impl Assembler { .insert(label, Some(self.concrete_len)) .expect("label should exist"); assert_eq!(old, None, "label should have been undefined"); - Ok(()) } - RawOp::Op(AbstractOp::MacroDefinition(_)) => Ok(()), - RawOp::Op(AbstractOp::Macro(_)) => Ok(()), + RawOp::Op(AbstractOp::MacroDefinition(_)) => {} + RawOp::Op(AbstractOp::Macro(ref m)) => { + self.expand_macro(&m.name, &m.parameters)?; + } RawOp::Op(ref op) => { match op .clone() @@ -450,13 +419,12 @@ impl Assembler { source: UnknownLabel { label: _label, .. }, }) => { let mut dynamic_push = false; - match op.size() { - Some(size) => self.concrete_len += size, - None => { - self.concrete_len += 2; - dynamic_push = true; - } - }; + if let AbstractOp::Push(_) = op { + dynamic_push = true; + self.concrete_len += 2; + } else { + self.concrete_len += op.size().unwrap(); + } self.undeclared_labels.push(PendingLabel { label: _label.to_owned(), @@ -472,22 +440,34 @@ impl Assembler { source: UndefinedVariable { name, .. }, }) => return error::UndeclaredVariableMacro { var: name }.fail(), } - - Ok(()) } RawOp::Raw(raw) => { self.concrete_len += raw.len(); self.ready.push(RawOp::Raw(raw)); - Ok(()) } RawOp::Scope(scope) => { let mut asm = Self::new(); - let scope_result = asm.assemble(scope)?; + let scope_result = asm.assemble(&scope)?; self.concrete_len += scope_result.len(); self.ready.push(RawOp::Raw(scope_result)); - Ok(()) } } + + Ok(self.concrete_len) + } + + fn declare_label(&mut self, rop: &RawOp) -> Result<(), Error> { + if let RawOp::Op(AbstractOp::Label(ref label)) = *rop { + match self.declared_labels.entry(label.to_owned()) { + hash_map::Entry::Occupied(_) => { + return error::DuplicateLabel { label }.fail(); + } + hash_map::Entry::Vacant(v) => { + v.insert(None); + } + } + } + Ok(()) } fn expand_macro( @@ -548,7 +528,7 @@ impl Assembler { } for op in m.contents.iter() { - self.push(op.clone())?; + self.push(op)?; } Ok(Some(self.concrete_len)) } @@ -572,11 +552,12 @@ mod tests { #[test] fn assemble_variable_push_const_while_pending() -> Result<(), Error> { let mut asm = Assembler::new(); - let result = asm.assemble(vec![ + let code = vec![ AbstractOp::Op(Push1(Imm::with_label("label1")).into()), AbstractOp::Push(Terminal::Number(0xaabb.into()).into()), AbstractOp::Label("label1".into()), - ])?; + ]; + let result = asm.assemble(&code)?; assert_eq!(result, hex!("600561aabb")); Ok(()) } @@ -584,7 +565,7 @@ mod tests { #[test] fn assemble_variable_pushes_abab() -> Result<(), Error> { let mut asm = Assembler::new(); - let result = asm.assemble(vec![ + let code = vec![ AbstractOp::new(JumpDest), AbstractOp::Push(Imm::with_label("label1")), AbstractOp::Push(Imm::with_label("label2")), @@ -592,7 +573,8 @@ mod tests { AbstractOp::new(GetPc), AbstractOp::Label("label2".into()), AbstractOp::new(GetPc), - ])?; + ]; + let result = asm.assemble(&code)?; assert_eq!(result, hex!("5b600560065858")); Ok(()) } @@ -600,7 +582,7 @@ mod tests { #[test] fn assemble_variable_pushes_abba() -> Result<(), Error> { let mut asm = Assembler::new(); - let result = asm.assemble(vec![ + let code = vec![ AbstractOp::new(JumpDest), AbstractOp::Push(Imm::with_label("label1")), AbstractOp::Push(Imm::with_label("label2")), @@ -608,7 +590,8 @@ mod tests { AbstractOp::new(GetPc), AbstractOp::Label("label1".into()), AbstractOp::new(GetPc), - ])?; + ]; + let result = asm.assemble(&code)?; assert_eq!(result, hex!("5b600660055858")); Ok(()) } @@ -616,12 +599,13 @@ mod tests { #[test] fn assemble_variable_push1_multiple() -> Result<(), Error> { let mut asm = Assembler::new(); - let result = asm.assemble(vec![ + let code = vec![ AbstractOp::new(JumpDest), AbstractOp::Push(Imm::with_label("auto")), AbstractOp::Push(Imm::with_label("auto")), AbstractOp::Label("auto".into()), - ])?; + ]; + let result = asm.assemble(&code)?; assert_eq!(result, hex!("5b60056005")); Ok(()) } @@ -629,9 +613,10 @@ mod tests { #[test] fn assemble_variable_push_const() -> Result<(), Error> { let mut asm = Assembler::new(); - let result = asm.assemble(vec![AbstractOp::Push( + let code = vec![AbstractOp::Push( Terminal::Number((0x00aaaaaaaaaaaaaaaaaaaaaaaa as u128).into()).into(), - )])?; + )]; + let result = asm.assemble(&code)?; assert_eq!(result, hex!("6baaaaaaaaaaaaaaaaaaaaaaaa")); Ok(()) } @@ -641,9 +626,8 @@ mod tests { let v = BigInt::from_bytes_be(Sign::Plus, &[1u8; 33]); let mut asm = Assembler::new(); - let err = asm - .assemble(vec![AbstractOp::Push(Terminal::Number(v).into())]) - .unwrap_err(); + let code = vec![AbstractOp::Push(Terminal::Number(v).into())]; + let err = asm.assemble(&code).unwrap_err(); assert_matches!(err, Error::ExpressionTooLarge { .. }); } @@ -651,9 +635,8 @@ mod tests { #[test] fn assemble_variable_push_negative() { let mut asm = Assembler::new(); - let err = asm - .assemble(vec![AbstractOp::Push(Terminal::Number((-1).into()).into())]) - .unwrap_err(); + let code = vec![AbstractOp::Push(Terminal::Number((-1).into()).into())]; + let err = asm.assemble(&code).unwrap_err(); assert_matches!(err, Error::ExpressionNegative { .. }); } @@ -661,9 +644,10 @@ mod tests { #[test] fn assemble_variable_push_const0() -> Result<(), Error> { let mut asm = Assembler::new(); - let result = asm.assemble(vec![AbstractOp::Push( + let code = vec![AbstractOp::Push( Terminal::Number((0x00 as u128).into()).into(), - )])?; + )]; + let result = asm.assemble(&code)?; assert_eq!(result, hex!("6000")); Ok(()) } @@ -671,11 +655,12 @@ mod tests { #[test] fn assemble_variable_push1_known() -> Result<(), Error> { let mut asm = Assembler::new(); - let result = asm.assemble(vec![ + let code = vec![ AbstractOp::new(JumpDest), AbstractOp::Label("auto".into()), AbstractOp::Push(Imm::with_label("auto")), - ])?; + ]; + let result = asm.assemble(&code)?; assert_eq!(result, hex!("5b6001")); Ok(()) } @@ -683,11 +668,12 @@ mod tests { #[test] fn assemble_variable_push1() -> Result<(), Error> { let mut asm = Assembler::new(); - let result = asm.assemble(vec![ + let code = vec![ AbstractOp::Push(Imm::with_label("auto")), AbstractOp::Label("auto".into()), AbstractOp::new(JumpDest), - ])?; + ]; + let result = asm.assemble(&code)?; assert_eq!(result, hex!("60025b")); Ok(()) } @@ -695,12 +681,13 @@ mod tests { #[test] fn assemble_variable_push1_reuse() -> Result<(), Error> { let mut asm = Assembler::new(); - let result = asm.assemble(vec![ + let code = vec![ AbstractOp::Push(Imm::with_label("auto")), AbstractOp::Label("auto".into()), AbstractOp::new(JumpDest), AbstractOp::new(Push1(Imm::with_label("auto"))), - ])?; + ]; + let result = asm.assemble(&code)?; assert_eq!(result, hex!("60025b6002")); Ok(()) } @@ -717,7 +704,7 @@ mod tests { code.push(AbstractOp::new(JumpDest)); let mut asm = Assembler::new(); - let result = asm.assemble(code)?; + let result = asm.assemble(&code)?; let mut expected = vec![0x61, 0x01, 0x02]; expected.extend_from_slice(&[0x58; 255]); @@ -730,9 +717,8 @@ mod tests { #[test] fn assemble_undeclared_label() -> Result<(), Error> { let mut asm = Assembler::new(); - let err = asm - .assemble(vec![AbstractOp::new(Push1(Imm::with_label("hi")))]) - .unwrap_err(); + let code = vec![AbstractOp::new(Push1(Imm::with_label("hi")))]; + let err = asm.assemble(&code).unwrap_err(); assert_matches!(err, Error::UndeclaredLabels { labels, .. } if labels == vec!["hi"]); Ok(()) } @@ -740,7 +726,8 @@ mod tests { #[test] fn assemble_jumpdest_no_label() -> Result<(), Error> { let mut asm = Assembler::new(); - let result = asm.assemble(vec![AbstractOp::new(JumpDest)])?; + let code = vec![AbstractOp::new(JumpDest)]; + let result = asm.assemble(&code)?; assert!(asm.declared_labels.is_empty()); assert_eq!(result, hex!("5b")); Ok(()) @@ -751,7 +738,7 @@ mod tests { let mut asm = Assembler::new(); let ops = vec![AbstractOp::Label("lbl".into()), AbstractOp::new(JumpDest)]; - let result = asm.assemble(ops)?; + let result = asm.assemble(&ops)?; assert_eq!(asm.declared_labels.len(), 1); assert_eq!(asm.declared_labels.get("lbl"), Some(&Some(0))); assert_eq!(result, hex!("5b")); @@ -767,7 +754,7 @@ mod tests { ]; let mut asm = Assembler::new(); - let result = asm.assemble(ops)?; + let result = asm.assemble(&ops)?; assert_eq!(result, hex!("5b6000")); Ok(()) @@ -782,7 +769,7 @@ mod tests { ]; let mut asm = Assembler::new(); - let result = asm.assemble(ops)?; + let result = asm.assemble(&ops)?; assert_eq!(result, hex!("600258")); Ok(()) @@ -797,7 +784,7 @@ mod tests { ]; let mut asm = Assembler::new(); - let result = asm.assemble(ops)?; + let result = asm.assemble(&ops)?; assert_eq!(result, hex!("60025b")); Ok(()) @@ -812,7 +799,7 @@ mod tests { ops.push(AbstractOp::new(JumpDest)); ops.push(AbstractOp::new(Push1(Imm::with_label("a")))); let mut asm = Assembler::new(); - let err = asm.assemble(ops).unwrap_err(); + let err = asm.assemble(&ops).unwrap_err(); assert_matches!(err, Error::ExpressionTooLarge { expr: Expression::Terminal(Terminal::Label(label)), .. } if label == "a"); } @@ -825,7 +812,7 @@ mod tests { ops.push(AbstractOp::new(JumpDest)); ops.push(AbstractOp::new(Push1(Imm::with_label("b")))); let mut asm = Assembler::new(); - let result = asm.assemble(ops)?; + let result = asm.assemble(&ops)?; let mut expected = vec![0x58; 255]; expected.push(0x5b); @@ -864,7 +851,7 @@ mod tests { ]; let mut asm = Assembler::new(); - let result = asm.assemble(ops)?; + let result = asm.assemble(&ops)?; assert_eq!(result, []); Ok(()) @@ -898,7 +885,7 @@ mod tests { ]; let mut asm = Assembler::new(); - let result = asm.assemble(ops)?; + let result = asm.assemble(&ops)?; assert_eq!(result, hex!("5b60005b600360005b60086000")); Ok(()) @@ -928,7 +915,7 @@ mod tests { ]; let mut asm = Assembler::new(); - let result = asm.assemble(ops)?; + let result = asm.assemble(&ops)?; assert_eq!(result, hex!("5b60005b60036000")); Ok(()) @@ -958,7 +945,7 @@ mod tests { ]; let mut asm = Assembler::new(); - let result = asm.assemble(ops)?; + let result = asm.assemble(&ops)?; assert_eq!(result, hex!("5b60005b60036000")); Ok(()) @@ -988,7 +975,7 @@ mod tests { ]; let mut asm = Assembler::new(); - let result = asm.assemble(ops)?; + let result = asm.assemble(&ops)?; assert_eq!(result, hex!("5b600560065858")); Ok(()) @@ -1000,7 +987,7 @@ mod tests { InstructionMacroInvocation::with_zero_parameters("my_macro".into()), )]; let mut asm = Assembler::new(); - let err = asm.assemble(ops).unwrap_err(); + let err = asm.assemble(&ops).unwrap_err(); assert_matches!(err, Error::UndeclaredInstructionMacro { name, .. } if name == "my_macro"); Ok(()) @@ -1023,7 +1010,7 @@ mod tests { .into(), ]; let mut asm = Assembler::new(); - let err = asm.assemble(ops).unwrap_err(); + let err = asm.assemble(&ops).unwrap_err(); assert_matches!(err, Error::DuplicateMacro { name, .. } if name == "my_macro"); Ok(()) @@ -1043,7 +1030,7 @@ mod tests { )), ]; let mut asm = Assembler::new(); - let err = asm.assemble(ops).unwrap_err(); + let err = asm.assemble(&ops).unwrap_err(); assert_matches!(err, Error::DuplicateLabel { label, .. } if label == "a"); Ok(()) @@ -1070,7 +1057,7 @@ mod tests { AbstractOp::new(Push1(Imm::with_label("a"))), ]; let mut asm = Assembler::new(); - let result = asm.assemble(ops)?; + let result = asm.assemble(&ops)?; assert_eq!(result, hex!("3360016000")); @@ -1102,7 +1089,7 @@ mod tests { ]; let mut asm = Assembler::new(); - let result = asm.assemble(ops)?; + let result = asm.assemble(&ops)?; assert_eq!(result, hex!("5b600060426000")); Ok(()) @@ -1115,7 +1102,7 @@ mod tests { )))]; let mut asm = Assembler::new(); - let result = asm.assemble(ops)?; + let result = asm.assemble(&ops)?; assert_eq!(result, hex!("6002")); Ok(()) @@ -1127,7 +1114,7 @@ mod tests { BigInt::from(-1).into(), )))]; let mut asm = Assembler::new(); - let err = asm.assemble(ops).unwrap_err(); + let err = asm.assemble(&ops).unwrap_err(); assert_matches!(err, Error::ExpressionNegative { value, .. } if value == BigInt::from(-1)); Ok(()) @@ -1136,11 +1123,10 @@ mod tests { #[test] fn assemble_expression_undeclared_label() -> Result<(), Error> { let mut asm = Assembler::new(); - let err = asm - .assemble(vec![AbstractOp::new(Push1(Imm::with_expression( - Terminal::Label(String::from("hi")).into(), - )))]) - .unwrap_err(); + let ops = vec![AbstractOp::new(Push1(Imm::with_expression( + Terminal::Label(String::from("hi")).into(), + )))]; + let err = asm.assemble(&ops).unwrap_err(); assert_matches!(err, Error::UndeclaredLabels { labels, .. } if labels == vec!["hi"]); Ok(()) } @@ -1148,16 +1134,15 @@ mod tests { #[test] fn assemble_variable_push_expression_with_undeclared_labels() -> Result<(), Error> { let mut asm = Assembler::new(); - let err = asm - .assemble(vec![ - AbstractOp::new(JumpDest), - AbstractOp::Push(Imm::with_expression(Expression::Plus( - Terminal::Label("foo".into()).into(), - Terminal::Label("bar".into()).into(), - ))), - AbstractOp::new(Gas), - ]) - .unwrap_err(); + let ops = vec![ + AbstractOp::new(JumpDest), + AbstractOp::Push(Imm::with_expression(Expression::Plus( + Terminal::Label("foo".into()).into(), + Terminal::Label("bar".into()).into(), + ))), + AbstractOp::new(Gas), + ]; + let err = asm.assemble(&ops).unwrap_err(); // The expressions have short-circuit evaluation, so only the first label is caught in the error. assert_matches!(err, Error::UndeclaredLabels { labels, .. } if (labels.contains(&"foo".to_string()))); Ok(()) @@ -1166,14 +1151,15 @@ mod tests { #[test] fn assemble_variable_push1_expression() -> Result<(), Error> { let mut asm = Assembler::new(); - let result = asm.assemble(vec![ + let ops = vec![ AbstractOp::new(JumpDest), AbstractOp::Label("auto".into()), AbstractOp::Push(Imm::with_expression(Expression::Plus( 1.into(), Terminal::Label(String::from("auto")).into(), ))), - ])?; + ]; + let result = asm.assemble(&ops)?; assert_eq!(result, hex!("5b6002")); Ok(()) } @@ -1181,7 +1167,7 @@ mod tests { #[test] fn assemble_expression_with_labels() -> Result<(), Error> { let mut asm = Assembler::new(); - let result = asm.assemble(vec![ + let ops = vec![ AbstractOp::new(JumpDest), AbstractOp::Push(Imm::with_expression(Expression::Plus( Terminal::Label(String::from("foo")).into(), @@ -1190,7 +1176,8 @@ mod tests { AbstractOp::new(Gas), AbstractOp::Label("foo".into()), AbstractOp::Label("bar".into()), - ])?; + ]; + let result = asm.assemble(&ops)?; assert_eq!(result, hex!("5b60085a")); Ok(()) } @@ -1211,7 +1198,7 @@ mod tests { ]; let mut asm = Assembler::new(); - let result = asm.assemble(ops)?; + let result = asm.assemble(&ops)?; assert_eq!(result, hex!("6002")); Ok(()) @@ -1236,7 +1223,7 @@ mod tests { ]; let mut asm = Assembler::new(); - let err = asm.assemble(ops).unwrap_err(); + let err = asm.assemble(&ops).unwrap_err(); assert_matches!(err, Error::UndeclaredVariableMacro { var, .. } if var == "bar"); } @@ -1268,7 +1255,7 @@ mod tests { ]; let mut asm = Assembler::new(); - let result = asm.assemble(ops)?; + let result = asm.assemble(&ops)?; assert_eq!(result, hex!("58335b")); Ok(()) @@ -1301,7 +1288,7 @@ mod tests { ]; let mut asm = Assembler::new(); - let result = asm.assemble(ops)?; + let result = asm.assemble(&ops)?; assert_eq!(result, hex!("585b33")); Ok(()) diff --git a/etk-asm/src/ingest.rs b/etk-asm/src/ingest.rs index f6ba6247..0cef990d 100644 --- a/etk-asm/src/ingest.rs +++ b/etk-asm/src/ingest.rs @@ -292,7 +292,7 @@ where let mut program = Program::new(path.into()); let nodes = self.preprocess(&mut program, src)?; let mut asm = Assembler::new(); - let raw = asm.assemble(nodes)?; + let raw = asm.assemble(&nodes)?; self.output.write_all(&raw).context(error::Io { message: "writing output", From c355aa2d0eaa8059de1ed4b8bb4b00ca6538b76e Mon Sep 17 00:00:00 2001 From: Gaston Zanitti Date: Mon, 30 Oct 2023 16:55:42 -0300 Subject: [PATCH 53/73] addressing comments --- etk-asm/src/asm.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/etk-asm/src/asm.rs b/etk-asm/src/asm.rs index 07271003..7c7121d9 100644 --- a/etk-asm/src/asm.rs +++ b/etk-asm/src/asm.rs @@ -176,7 +176,7 @@ impl From> for RawOp { impl> From<&O> for RawOp { fn from(item: &O) -> Self { - item.clone().into() + item.into() } } @@ -319,7 +319,7 @@ impl Assembler { where O: Into, { - self.inspect_macros(&ops)?; + self.inspect_macros(ops)?; for op in ops { self.push(op)?; From 27fc550804f7c07a75fa2eb9bbb1dac73c82de08 Mon Sep 17 00:00:00 2001 From: Gaston Zanitti Date: Tue, 31 Oct 2023 11:18:43 -0300 Subject: [PATCH 54/73] Dynamic push: new test --- etk-asm/src/asm.rs | 44 +++++++++++------------ etk-asm/tests/asm.rs | 13 +++++++ etk-asm/tests/asm/variable-push2/main.etk | 3 ++ 3 files changed, 38 insertions(+), 22 deletions(-) create mode 100644 etk-asm/tests/asm/variable-push2/main.etk diff --git a/etk-asm/src/asm.rs b/etk-asm/src/asm.rs index 7c7121d9..1786d89d 100644 --- a/etk-asm/src/asm.rs +++ b/etk-asm/src/asm.rs @@ -142,7 +142,7 @@ mod error { pub use self::error::Error; use crate::ops::expression::Error::{UndefinedVariable, UnknownLabel, UnknownMacro}; -use crate::ops::{self, AbstractOp, Assemble, Expression, MacroDefinition}; +use crate::ops::{self, AbstractOp, Assemble, Expression, Imm, MacroDefinition}; use rand::Rng; use std::cmp; use std::collections::{hash_map, HashMap}; @@ -174,12 +174,6 @@ impl From> for RawOp { } } -impl> From<&O> for RawOp { - fn from(item: &O) -> Self { - item.into() - } -} - /// Assembles a series of [`RawOp`] into raw bytes, tracking and resolving macros and labels, /// and handling dynamic pushes. /// @@ -226,6 +220,9 @@ struct PendingLabel { /// Position where the label was invoked. position: usize, + /// The immediate value of the label invocation. + imm: Option, + /// Whether the label was invoked with a dynamic push or not. dynamic_push: bool, } @@ -239,12 +236,12 @@ impl Assembler { /// Inspect the macros in a series of [`RawOp`] and declare them in the assembler. fn inspect_macros(&mut self, nodes: &[O]) -> Result<(), Error> where - O: Into, + O: Into + Clone, //TODO: Remove clone and fix code { - for op in nodes { - let op = op.into(); - if let RawOp::Op(AbstractOp::MacroDefinition(_)) = op { - self.declare_macro(op)? + for op in nodes.iter() { + let raw_op: RawOp = (*op).clone().into(); + if let RawOp::Op(AbstractOp::MacroDefinition(_)) = raw_op { + self.declare_macro(raw_op)? } } @@ -317,7 +314,7 @@ impl Assembler { /// Returns the code of the assembled program. pub fn assemble(&mut self, ops: &[O]) -> Result, Error> where - O: Into, + O: Into + Clone, //TODO: Remove clone and fix code { self.inspect_macros(ops)?; @@ -355,9 +352,10 @@ impl Assembler { /// Feed a single instruction into the `Assembler`. fn push(&mut self, rop: &O) -> Result where - O: Into, + O: Into + Clone, { - let rop = rop.into(); + //let rop = rop.into(); + let rop = rop.clone().into(); self.declare_label(&rop)?; @@ -367,17 +365,16 @@ impl Assembler { for ul in self.undeclared_labels.iter() { if (ul.label == label) & ul.dynamic_push { // Compensation in case label was dynamically pushed. - let mut tmp = - ((self.concrete_len as f32 - ul.position as f32) / 256.0).floor(); + let mut dst_tmp = (self.concrete_len - ul.position) / 256; // Size already accounted for %push(label) was 2. - if tmp > 1.0 { - tmp -= 1.0; + if dst_tmp > 1 { + dst_tmp -= 1; } - dst = cmp::max(tmp as usize, dst); + + dst = cmp::max(dst_tmp, dst); } } - self.undeclared_labels.retain(|l| l.label != *label); self.concrete_len += dst; @@ -419,9 +416,11 @@ impl Assembler { source: UnknownLabel { label: _label, .. }, }) => { let mut dynamic_push = false; - if let AbstractOp::Push(_) = op { + let mut imm: Option = None; + if let AbstractOp::Push(inner) = op { dynamic_push = true; self.concrete_len += 2; + imm = Some(inner.clone()); } else { self.concrete_len += op.size().unwrap(); } @@ -429,6 +428,7 @@ impl Assembler { self.undeclared_labels.push(PendingLabel { label: _label.to_owned(), position: self.ready.len(), + imm, dynamic_push, }); self.ready.push(rop); diff --git a/etk-asm/tests/asm.rs b/etk-asm/tests/asm.rs index c09dba9f..88ae6b9b 100644 --- a/etk-asm/tests/asm.rs +++ b/etk-asm/tests/asm.rs @@ -303,3 +303,16 @@ fn test_dynamic_push_and_include() -> Result<(), Error> { Ok(()) } + +#[test] +fn test_dynamic_push2() -> Result<(), Error> { + let mut output = Vec::new(); + let mut ingester = Ingest::new(&mut output); + ingester.ingest_file(source(&["variable-push2", "main.etk"]))?; + + println!("{:x?}", output); + println!("{:?}", output); + assert_eq!(output, hex!("61010058")); + + Ok(()) +} diff --git a/etk-asm/tests/asm/variable-push2/main.etk b/etk-asm/tests/asm/variable-push2/main.etk new file mode 100644 index 00000000..618285ee --- /dev/null +++ b/etk-asm/tests/asm/variable-push2/main.etk @@ -0,0 +1,3 @@ +%push(label + 254) +label: +pc \ No newline at end of file From a549703cccb730b65a35a940917a6dffcfb0f146 Mon Sep 17 00:00:00 2001 From: Gaston Zanitti Date: Tue, 31 Oct 2023 11:20:02 -0300 Subject: [PATCH 55/73] Dynamic push: new test --- etk-asm/src/asm.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/etk-asm/src/asm.rs b/etk-asm/src/asm.rs index 1786d89d..a6158e31 100644 --- a/etk-asm/src/asm.rs +++ b/etk-asm/src/asm.rs @@ -220,9 +220,6 @@ struct PendingLabel { /// Position where the label was invoked. position: usize, - /// The immediate value of the label invocation. - imm: Option, - /// Whether the label was invoked with a dynamic push or not. dynamic_push: bool, } @@ -416,11 +413,9 @@ impl Assembler { source: UnknownLabel { label: _label, .. }, }) => { let mut dynamic_push = false; - let mut imm: Option = None; - if let AbstractOp::Push(inner) = op { + if let AbstractOp::Push(_) = op { dynamic_push = true; self.concrete_len += 2; - imm = Some(inner.clone()); } else { self.concrete_len += op.size().unwrap(); } @@ -428,7 +423,6 @@ impl Assembler { self.undeclared_labels.push(PendingLabel { label: _label.to_owned(), position: self.ready.len(), - imm, dynamic_push, }); self.ready.push(rop); From 91a797e0acc62022ae916e55d6c7cfaf02dcb41b Mon Sep 17 00:00:00 2001 From: Gaston Zanitti Date: Wed, 8 Nov 2023 12:46:25 -0300 Subject: [PATCH 56/73] Minor fix and new test --- etk-asm/src/asm.rs | 10 +++++----- etk-asm/tests/asm.rs | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/etk-asm/src/asm.rs b/etk-asm/src/asm.rs index a6158e31..83dccc20 100644 --- a/etk-asm/src/asm.rs +++ b/etk-asm/src/asm.rs @@ -142,7 +142,7 @@ mod error { pub use self::error::Error; use crate::ops::expression::Error::{UndefinedVariable, UnknownLabel, UnknownMacro}; -use crate::ops::{self, AbstractOp, Assemble, Expression, Imm, MacroDefinition}; +use crate::ops::{self, AbstractOp, Assemble, Expression, MacroDefinition}; use rand::Rng; use std::cmp; use std::collections::{hash_map, HashMap}; @@ -187,7 +187,7 @@ impl From> for RawOp { /// # /// # use hex_literal::hex; /// let mut asm = Assembler::new(); -/// let code = ec![AbstractOp::new(GetPc)] +/// let code = vec![AbstractOp::new(GetPc)]; /// let result = asm.assemble(&code)?; /// # assert_eq!(result, hex!("58")); /// # Result::<(), Error>::Ok(()) @@ -349,7 +349,7 @@ impl Assembler { /// Feed a single instruction into the `Assembler`. fn push(&mut self, rop: &O) -> Result where - O: Into + Clone, + O: Into + Clone, //TODO: Remove clone and fix code { //let rop = rop.into(); let rop = rop.clone().into(); @@ -410,7 +410,7 @@ impl Assembler { .fail() } Err(ops::Error::ContextIncomplete { - source: UnknownLabel { label: _label, .. }, + source: UnknownLabel { label, .. }, }) => { let mut dynamic_push = false; if let AbstractOp::Push(_) = op { @@ -421,7 +421,7 @@ impl Assembler { } self.undeclared_labels.push(PendingLabel { - label: _label.to_owned(), + label, position: self.ready.len(), dynamic_push, }); diff --git a/etk-asm/tests/asm.rs b/etk-asm/tests/asm.rs index 88ae6b9b..c7d659c1 100644 --- a/etk-asm/tests/asm.rs +++ b/etk-asm/tests/asm.rs @@ -312,7 +312,7 @@ fn test_dynamic_push2() -> Result<(), Error> { println!("{:x?}", output); println!("{:?}", output); - assert_eq!(output, hex!("61010058")); + assert_eq!(output, hex!("61010158")); Ok(()) } From 26ff19df31738c5b2a005715485e0d70a214547b Mon Sep 17 00:00:00 2001 From: Gaston Zanitti Date: Thu, 9 Nov 2023 12:55:51 -0300 Subject: [PATCH 57/73] Fix issue with labels + expressions in dynamic push --- Cargo.lock | 5 +- etk-asm/Cargo.toml | 17 ++++-- etk-asm/src/asm.rs | 55 ++++++++++++++------ etk-asm/tests/asm.rs | 17 ++++-- etk-asm/tests/asm/variable-push/included.etk | 2 +- etk-asm/tests/asm/variable-push/main.etk | 3 +- etk-asm/tests/asm/variable-push2/main.etk | 3 -- etk-asm/tests/asm/variable-push2/main1.etk | 3 ++ etk-asm/tests/asm/variable-push2/main2.etk | 3 ++ etk-asm/tests/asm/variable-push2/main3.etk | 6 +++ 10 files changed, 81 insertions(+), 33 deletions(-) delete mode 100644 etk-asm/tests/asm/variable-push2/main.etk create mode 100644 etk-asm/tests/asm/variable-push2/main1.etk create mode 100644 etk-asm/tests/asm/variable-push2/main2.etk create mode 100644 etk-asm/tests/asm/variable-push2/main3.etk diff --git a/Cargo.lock b/Cargo.lock index d2a9264e..1f8d0a38 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -439,6 +439,7 @@ dependencies = [ "hex", "hex-literal", "num-bigint", + "num-traits", "pest", "pest_derive", "rand", @@ -936,9 +937,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" dependencies = [ "autocfg", ] diff --git a/etk-asm/Cargo.toml b/etk-asm/Cargo.toml index a479bd3e..e2b4622d 100644 --- a/etk-asm/Cargo.toml +++ b/etk-asm/Cargo.toml @@ -1,7 +1,10 @@ [package] name = "etk-asm" version = "0.4.0-dev" -authors = ["Sam Wilson ", "lightclient "] +authors = [ + "Sam Wilson ", + "lightclient ", +] license = "MIT OR Apache-2.0" edition = "2018" description = "EVM Toolkit assembler" @@ -9,23 +12,29 @@ homepage = "https://quilt.github.io/etk" repository = "https://github.com/quilt/etk" readme = "README.md" keywords = ["etk", "ethereum", "assembler"] -categories = ["cryptography::cryptocurrencies", "command-line-utilities", "development-tools", "compilers"] +categories = [ + "cryptography::cryptocurrencies", + "command-line-utilities", + "development-tools", + "compilers", +] [features] cli = ["clap", "etk-cli"] -backtraces = [ "snafu/backtraces", "etk-ops/backtraces" ] +backtraces = ["snafu/backtraces", "etk-ops/backtraces"] [dependencies] etk-ops = { path = "../etk-ops", version = "0.4.0-dev" } etk-cli = { optional = true, path = "../etk-cli", version = "0.4.0-dev" } hex = "0.4.3" num-bigint = "0.4" +num-traits = "0.2.17" pest = "2.1.3" pest_derive = "2.1" rand = "0.8.5" sha3 = "0.10.1" clap = { optional = true, version = "3.1", features = ["derive"] } -snafu = { version = "0.7.1", default-features = false, features = [ "std" ] } +snafu = { version = "0.7.1", default-features = false, features = ["std"] } [dev-dependencies] assert_matches = "1.5.0" diff --git a/etk-asm/src/asm.rs b/etk-asm/src/asm.rs index 83dccc20..e7a002d4 100644 --- a/etk-asm/src/asm.rs +++ b/etk-asm/src/asm.rs @@ -143,9 +143,10 @@ mod error { pub use self::error::Error; use crate::ops::expression::Error::{UndefinedVariable, UnknownLabel, UnknownMacro}; use crate::ops::{self, AbstractOp, Assemble, Expression, MacroDefinition}; +use num_traits::ToPrimitive; use rand::Rng; use std::cmp; -use std::collections::{hash_map, HashMap}; +use std::collections::{hash_map, HashMap, HashSet}; /// An item to be assembled, which can be either an [`AbstractOp`], /// the inclusion of a new scope or a raw byte sequence. @@ -254,7 +255,42 @@ impl Assembler { .clone() .concretize((&self.declared_labels, &self.declared_macros).into()) { - Ok(cop) => cop.assemble(&mut output), + Ok(mut cop) => { + if let AbstractOp::Push(imm) = op { + let exp = imm.tree.eval_with_context( + (&self.declared_labels, &self.declared_macros).into(), + ); + + let labels: HashSet = imm + .tree + .labels(&self.declared_macros) + .unwrap() + .into_iter() + .collect(); + if labels.len() > 0 { + if let Ok(val) = exp { + let extra = val.to_usize().unwrap() / 256; + if extra > 0 { + self.concrete_len += extra as usize; + + for label in labels { + if let Some(position) = + self.declared_labels.get_mut(&label) + { + *position = position.map(|v| v + 1); + } + } + } + } + } + + cop = op + .clone() + .concretize((&self.declared_labels, &self.declared_macros).into()) + .unwrap(); + } + cop.assemble(&mut output) + } Err(ops::Error::ContextIncomplete { source: UnknownLabel { .. }, }) => { @@ -358,22 +394,7 @@ impl Assembler { match rop { RawOp::Op(AbstractOp::Label(label)) => { - let mut dst = 0; - for ul in self.undeclared_labels.iter() { - if (ul.label == label) & ul.dynamic_push { - // Compensation in case label was dynamically pushed. - let mut dst_tmp = (self.concrete_len - ul.position) / 256; - - // Size already accounted for %push(label) was 2. - if dst_tmp > 1 { - dst_tmp -= 1; - } - - dst = cmp::max(dst_tmp, dst); - } - } self.undeclared_labels.retain(|l| l.label != *label); - self.concrete_len += dst; let old = self .declared_labels diff --git a/etk-asm/tests/asm.rs b/etk-asm/tests/asm.rs index c7d659c1..07302f6b 100644 --- a/etk-asm/tests/asm.rs +++ b/etk-asm/tests/asm.rs @@ -299,7 +299,9 @@ fn test_dynamic_push_and_include() -> Result<(), Error> { let mut ingester = Ingest::new(&mut output); ingester.ingest_file(source(&["variable-push", "main.etk"]))?; - assert_eq!(output, hex!("61025758585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585801010101010158585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858015b58")); + let tmp: Vec = output.iter().map(|&num| format!("{:x?}", num)).collect(); + println!("{:?}", tmp.join("")); + assert_eq!(output, hex!("61025758585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585801010101010158585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858015b")); Ok(()) } @@ -308,11 +310,18 @@ fn test_dynamic_push_and_include() -> Result<(), Error> { fn test_dynamic_push2() -> Result<(), Error> { let mut output = Vec::new(); let mut ingester = Ingest::new(&mut output); - ingester.ingest_file(source(&["variable-push2", "main.etk"]))?; + ingester.ingest_file(source(&["variable-push2", "main1.etk"]))?; + assert_eq!(output, hex!("61010158")); - println!("{:x?}", output); - println!("{:?}", output); + let mut output = Vec::new(); + let mut ingester = Ingest::new(&mut output); + ingester.ingest_file(source(&["variable-push2", "main2.etk"]))?; assert_eq!(output, hex!("61010158")); + let mut output = Vec::new(); + let mut ingester = Ingest::new(&mut output); + ingester.ingest_file(source(&["variable-push2", "main3.etk"]))?; + assert_eq!(output, hex!("610107015801")); + Ok(()) } diff --git a/etk-asm/tests/asm/variable-push/included.etk b/etk-asm/tests/asm/variable-push/included.etk index c55517ab..db4c91e2 100644 --- a/etk-asm/tests/asm/variable-push/included.etk +++ b/etk-asm/tests/asm/variable-push/included.etk @@ -591,4 +591,4 @@ pc pc pc pc -add +add \ No newline at end of file diff --git a/etk-asm/tests/asm/variable-push/main.etk b/etk-asm/tests/asm/variable-push/main.etk index cef9faf7..08144f7c 100644 --- a/etk-asm/tests/asm/variable-push/main.etk +++ b/etk-asm/tests/asm/variable-push/main.etk @@ -3,5 +3,4 @@ pc pc %include("included.etk") label: -jumpdest -pc \ No newline at end of file +jumpdest \ No newline at end of file diff --git a/etk-asm/tests/asm/variable-push2/main.etk b/etk-asm/tests/asm/variable-push2/main.etk deleted file mode 100644 index 618285ee..00000000 --- a/etk-asm/tests/asm/variable-push2/main.etk +++ /dev/null @@ -1,3 +0,0 @@ -%push(label + 254) -label: -pc \ No newline at end of file diff --git a/etk-asm/tests/asm/variable-push2/main1.etk b/etk-asm/tests/asm/variable-push2/main1.etk new file mode 100644 index 00000000..7c5eb360 --- /dev/null +++ b/etk-asm/tests/asm/variable-push2/main1.etk @@ -0,0 +1,3 @@ +push2 label + 254 +label: +pc \ No newline at end of file diff --git a/etk-asm/tests/asm/variable-push2/main2.etk b/etk-asm/tests/asm/variable-push2/main2.etk new file mode 100644 index 00000000..10fdca9d --- /dev/null +++ b/etk-asm/tests/asm/variable-push2/main2.etk @@ -0,0 +1,3 @@ +%push(label + 254) +label: +pc \ No newline at end of file diff --git a/etk-asm/tests/asm/variable-push2/main3.etk b/etk-asm/tests/asm/variable-push2/main3.etk new file mode 100644 index 00000000..7c1f1d0e --- /dev/null +++ b/etk-asm/tests/asm/variable-push2/main3.etk @@ -0,0 +1,6 @@ +%push(label1 + label2 + 254) +add +label1: + pc +label2: + add \ No newline at end of file From e904798333576cb65b1306897afc90ffc15ffdb1 Mon Sep 17 00:00:00 2001 From: Gaston Zanitti Date: Thu, 9 Nov 2023 15:27:43 -0300 Subject: [PATCH 58/73] Fix issue with labels + expressions in dynamic push (rustfmt) --- etk-asm/src/asm.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/etk-asm/src/asm.rs b/etk-asm/src/asm.rs index e7a002d4..8ae6ba2e 100644 --- a/etk-asm/src/asm.rs +++ b/etk-asm/src/asm.rs @@ -267,11 +267,11 @@ impl Assembler { .unwrap() .into_iter() .collect(); - if labels.len() > 0 { + if !labels.is_empty() { if let Ok(val) = exp { let extra = val.to_usize().unwrap() / 256; if extra > 0 { - self.concrete_len += extra as usize; + self.concrete_len += extra; for label in labels { if let Some(position) = From 8cc1b5fc6beeae309648b3f39a4fdb24889e7326 Mon Sep 17 00:00:00 2001 From: Gaston Zanitti Date: Thu, 9 Nov 2023 16:48:24 -0300 Subject: [PATCH 59/73] Minor changes --- etk-asm/src/asm.rs | 63 ++++++++++++++++++---------------------------- 1 file changed, 25 insertions(+), 38 deletions(-) diff --git a/etk-asm/src/asm.rs b/etk-asm/src/asm.rs index 8ae6ba2e..3347768c 100644 --- a/etk-asm/src/asm.rs +++ b/etk-asm/src/asm.rs @@ -145,7 +145,6 @@ use crate::ops::expression::Error::{UndefinedVariable, UnknownLabel, UnknownMacr use crate::ops::{self, AbstractOp, Assemble, Expression, MacroDefinition}; use num_traits::ToPrimitive; use rand::Rng; -use std::cmp; use std::collections::{hash_map, HashMap, HashSet}; /// An item to be assembled, which can be either an [`AbstractOp`], @@ -175,6 +174,12 @@ impl From> for RawOp { } } +impl Into for &AbstractOp { + fn into(self) -> RawOp { + RawOp::Op(self.clone()) + } +} + /// Assembles a series of [`RawOp`] into raw bytes, tracking and resolving macros and labels, /// and handling dynamic pushes. /// @@ -217,12 +222,6 @@ pub struct Assembler { struct PendingLabel { /// The name of the label. label: String, - - /// Position where the label was invoked. - position: usize, - - /// Whether the label was invoked with a dynamic push or not. - dynamic_push: bool, } impl Assembler { @@ -234,10 +233,10 @@ impl Assembler { /// Inspect the macros in a series of [`RawOp`] and declare them in the assembler. fn inspect_macros(&mut self, nodes: &[O]) -> Result<(), Error> where - O: Into + Clone, //TODO: Remove clone and fix code + O: Into + Clone, { for op in nodes.iter() { - let raw_op: RawOp = (*op).clone().into(); + let raw_op: RawOp = op.clone().into(); if let RawOp::Op(AbstractOp::MacroDefinition(_)) = raw_op { self.declare_macro(raw_op)? } @@ -347,12 +346,12 @@ impl Assembler { /// Returns the code of the assembled program. pub fn assemble(&mut self, ops: &[O]) -> Result, Error> where - O: Into + Clone, //TODO: Remove clone and fix code + O: Into + Clone, { self.inspect_macros(ops)?; for op in ops { - self.push(op)?; + self.push(op.clone().into())?; } self.finish()?; @@ -363,11 +362,8 @@ impl Assembler { } /// Insert explicitly declared macros, via `AbstractOp`, into the `Assembler`. - fn declare_macro(&mut self, rop: O) -> Result<(), Error> - where - O: Into, - { - let rop = rop.into(); + fn declare_macro(&mut self, rop: RawOp) -> Result<(), Error> { + //let rop = rop.into(); if let RawOp::Op(AbstractOp::MacroDefinition(ref defn)) = rop { match self.declared_macros.entry(defn.name().to_owned()) { hash_map::Entry::Occupied(_) => { @@ -383,13 +379,11 @@ impl Assembler { } /// Feed a single instruction into the `Assembler`. - fn push(&mut self, rop: &O) -> Result + fn push(&mut self, rop: O) -> Result where - O: Into + Clone, //TODO: Remove clone and fix code + O: Into, { - //let rop = rop.into(); - let rop = rop.clone().into(); - + let rop = rop.into(); self.declare_label(&rop)?; match rop { @@ -413,7 +407,7 @@ impl Assembler { { Ok(cop) => { self.concrete_len += cop.size(); - self.ready.push(rop) + self.ready.push(rop.clone()) } Err(ops::Error::ExpressionTooLarge { value, spec, .. }) => { return error::ExpressionTooLarge { @@ -433,20 +427,14 @@ impl Assembler { Err(ops::Error::ContextIncomplete { source: UnknownLabel { label, .. }, }) => { - let mut dynamic_push = false; if let AbstractOp::Push(_) = op { - dynamic_push = true; self.concrete_len += 2; } else { self.concrete_len += op.size().unwrap(); } - self.undeclared_labels.push(PendingLabel { - label, - position: self.ready.len(), - dynamic_push, - }); - self.ready.push(rop); + self.undeclared_labels.push(PendingLabel { label }); + self.ready.push(rop.clone()); } Err(ops::Error::ContextIncomplete { source: UnknownMacro { name, .. }, @@ -458,7 +446,7 @@ impl Assembler { } RawOp::Raw(raw) => { self.concrete_len += raw.len(); - self.ready.push(RawOp::Raw(raw)); + self.ready.push(RawOp::Raw(raw.to_vec())); } RawOp::Scope(scope) => { let mut asm = Self::new(); @@ -472,15 +460,14 @@ impl Assembler { } fn declare_label(&mut self, rop: &RawOp) -> Result<(), Error> { - if let RawOp::Op(AbstractOp::Label(ref label)) = *rop { - match self.declared_labels.entry(label.to_owned()) { - hash_map::Entry::Occupied(_) => { - return error::DuplicateLabel { label }.fail(); - } - hash_map::Entry::Vacant(v) => { - v.insert(None); + if let RawOp::Op(AbstractOp::Label(label)) = rop { + if self.declared_labels.contains_key(label) { + return error::DuplicateLabel { + label: label.to_owned(), } + .fail(); } + self.declared_labels.insert(label.to_owned(), None); } Ok(()) } From 62b3510a99182f6fade763dcd88d5ec80a383aab Mon Sep 17 00:00:00 2001 From: Gaston Zanitti Date: Thu, 9 Nov 2023 16:52:03 -0300 Subject: [PATCH 60/73] PendingLabel removed --- etk-asm/src/asm.rs | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/etk-asm/src/asm.rs b/etk-asm/src/asm.rs index 3347768c..7e51cd55 100644 --- a/etk-asm/src/asm.rs +++ b/etk-asm/src/asm.rs @@ -214,14 +214,7 @@ pub struct Assembler { /// Labels that have been referred to (ex. with push) but /// have not been declared with an `AbstractOp::Label`. - undeclared_labels: Vec, -} - -/// Struct used to keep track of pending label invocations and their positions in code. -#[derive(Debug, Clone)] -struct PendingLabel { - /// The name of the label. - label: String, + undeclared_labels: Vec, } impl Assembler { @@ -296,7 +289,7 @@ impl Assembler { let undeclared_names: Vec<_> = self .undeclared_labels .iter() - .map(|PendingLabel { label, .. }| label.clone()) + .map(|label| label.clone()) .collect(); return error::UndeclaredLabels { labels: undeclared_names, @@ -332,7 +325,7 @@ impl Assembler { labels: self .undeclared_labels .iter() - .map(|l| l.label.to_owned()) + .map(|l| l.to_owned()) .collect::>(), } .fail(); @@ -388,7 +381,7 @@ impl Assembler { match rop { RawOp::Op(AbstractOp::Label(label)) => { - self.undeclared_labels.retain(|l| l.label != *label); + self.undeclared_labels.retain(|l| *l != label); let old = self .declared_labels @@ -433,7 +426,7 @@ impl Assembler { self.concrete_len += op.size().unwrap(); } - self.undeclared_labels.push(PendingLabel { label }); + self.undeclared_labels.push(label); self.ready.push(rop.clone()); } Err(ops::Error::ContextIncomplete { From 02492b268bd8cd94c4fb0bdb143cb09eee0c0e7a Mon Sep 17 00:00:00 2001 From: Gaston Zanitti Date: Thu, 9 Nov 2023 17:02:23 -0300 Subject: [PATCH 61/73] Into -> From for &AbstractOp --- etk-asm/src/asm.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/etk-asm/src/asm.rs b/etk-asm/src/asm.rs index 7e51cd55..e523996a 100644 --- a/etk-asm/src/asm.rs +++ b/etk-asm/src/asm.rs @@ -174,9 +174,9 @@ impl From> for RawOp { } } -impl Into for &AbstractOp { - fn into(self) -> RawOp { - RawOp::Op(self.clone()) +impl From<&AbstractOp> for RawOp { + fn from(op: &AbstractOp) -> Self { + Self::Op(op.clone()) } } From 4dcfd943148f595328e2372d372f6cbcd0201f72 Mon Sep 17 00:00:00 2001 From: Gaston Zanitti Date: Thu, 9 Nov 2023 17:19:06 -0300 Subject: [PATCH 62/73] Better naming. Code reorg. --- etk-asm/src/asm.rs | 80 +++++++++++++++++++--------------------------- 1 file changed, 33 insertions(+), 47 deletions(-) diff --git a/etk-asm/src/asm.rs b/etk-asm/src/asm.rs index e523996a..79fef9c9 100644 --- a/etk-asm/src/asm.rs +++ b/etk-asm/src/asm.rs @@ -223,23 +223,20 @@ impl Assembler { Self::default() } - /// Inspect the macros in a series of [`RawOp`] and declare them in the assembler. - fn inspect_macros(&mut self, nodes: &[O]) -> Result<(), Error> - where - O: Into + Clone, - { - for op in nodes.iter() { - let raw_op: RawOp = op.clone().into(); - if let RawOp::Op(AbstractOp::MacroDefinition(_)) = raw_op { - self.declare_macro(raw_op)? + /// Backpatch dynamic operations and emit the assembled program. + /// + /// Errors if there are any undeclared labels. + fn backpatch_and_emit(&mut self) -> Result, Error> { + if !self.undeclared_labels.is_empty() { + return error::UndeclaredLabels { + labels: self + .undeclared_labels + .iter() + .map(|l| l.to_owned()) + .collect::>(), } + .fail(); } - - Ok(()) - } - - /// Concretize all assembled instructions. - fn concretize_ops(&mut self) -> Result, Error> { let mut output = Vec::new(); for op in self.ready.iter() { if let RawOp::Op(ref op) = op { @@ -317,18 +314,25 @@ impl Assembler { Ok(output) } - /// Check if the input sequence is complete. Returns any errors that - /// may remain. - fn finish(&mut self) -> Result<(), Error> { - if !self.undeclared_labels.is_empty() { - return error::UndeclaredLabels { - labels: self - .undeclared_labels - .iter() - .map(|l| l.to_owned()) - .collect::>(), + /// Pre-define macros, via `AbstractOp`, into the `Assembler`. + /// + /// This is used to define macros that are used in the same scope. + fn pre_define_macros(&mut self, ops: &[O]) -> Result<(), Error> + where + O: Into + Clone, + { + for op in ops { + let rop = op.clone().into(); + if let RawOp::Op(AbstractOp::MacroDefinition(ref defn)) = rop { + match self.declared_macros.entry(defn.name().to_owned()) { + hash_map::Entry::Occupied(_) => { + return error::DuplicateMacro { name: defn.name() }.fail() + } + hash_map::Entry::Vacant(v) => { + v.insert(defn.to_owned()); + } + } } - .fail(); } Ok(()) @@ -341,36 +345,18 @@ impl Assembler { where O: Into + Clone, { - self.inspect_macros(ops)?; + //self.inspect_macros(ops)?; + self.pre_define_macros(ops)?; for op in ops { self.push(op.clone().into())?; } - self.finish()?; - - let output = self.concretize_ops()?; + let output = self.backpatch_and_emit()?; self.ready.clear(); Ok(output) } - /// Insert explicitly declared macros, via `AbstractOp`, into the `Assembler`. - fn declare_macro(&mut self, rop: RawOp) -> Result<(), Error> { - //let rop = rop.into(); - if let RawOp::Op(AbstractOp::MacroDefinition(ref defn)) = rop { - match self.declared_macros.entry(defn.name().to_owned()) { - hash_map::Entry::Occupied(_) => { - return error::DuplicateMacro { name: defn.name() }.fail() - } - hash_map::Entry::Vacant(v) => { - v.insert(defn.to_owned()); - } - } - } - - Ok(()) - } - /// Feed a single instruction into the `Assembler`. fn push(&mut self, rop: O) -> Result where From 9fd54c9dd53d3fb6d00e10d6353e430155c3ee28 Mon Sep 17 00:00:00 2001 From: Gaston Zanitti Date: Thu, 9 Nov 2023 17:24:08 -0300 Subject: [PATCH 63/73] Better naming. Code reorg. --- etk-asm/src/asm.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/etk-asm/src/asm.rs b/etk-asm/src/asm.rs index 79fef9c9..32f9cdfe 100644 --- a/etk-asm/src/asm.rs +++ b/etk-asm/src/asm.rs @@ -317,7 +317,7 @@ impl Assembler { /// Pre-define macros, via `AbstractOp`, into the `Assembler`. /// /// This is used to define macros that are used in the same scope. - fn pre_define_macros(&mut self, ops: &[O]) -> Result<(), Error> + fn declare_macros(&mut self, ops: &[O]) -> Result<(), Error> where O: Into + Clone, { @@ -345,8 +345,7 @@ impl Assembler { where O: Into + Clone, { - //self.inspect_macros(ops)?; - self.pre_define_macros(ops)?; + self.declare_macros(ops)?; for op in ops { self.push(op.clone().into())?; From 8296d16f892bf16497ab2c922a2045adfb623800 Mon Sep 17 00:00:00 2001 From: Gaston Zanitti Date: Thu, 9 Nov 2023 17:47:13 -0300 Subject: [PATCH 64/73] Better naming. --- etk-asm/src/asm.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/etk-asm/src/asm.rs b/etk-asm/src/asm.rs index 32f9cdfe..d74e39d7 100644 --- a/etk-asm/src/asm.rs +++ b/etk-asm/src/asm.rs @@ -283,13 +283,8 @@ impl Assembler { Err(ops::Error::ContextIncomplete { source: UnknownLabel { .. }, }) => { - let undeclared_names: Vec<_> = self - .undeclared_labels - .iter() - .map(|label| label.clone()) - .collect(); return error::UndeclaredLabels { - labels: undeclared_names, + labels: self.undeclared_labels.to_vec(), } .fail(); } From f9bc8ccfd2210a8500f265316cc3123510f3259e Mon Sep 17 00:00:00 2001 From: Gaston Zanitti Date: Thu, 9 Nov 2023 18:17:13 -0300 Subject: [PATCH 65/73] Code reorg. --- etk-asm/src/asm.rs | 194 ++++++++++++++++++++++----------------------- 1 file changed, 97 insertions(+), 97 deletions(-) diff --git a/etk-asm/src/asm.rs b/etk-asm/src/asm.rs index d74e39d7..195ee683 100644 --- a/etk-asm/src/asm.rs +++ b/etk-asm/src/asm.rs @@ -223,89 +223,21 @@ impl Assembler { Self::default() } - /// Backpatch dynamic operations and emit the assembled program. + /// Feed instructions into the `Assembler`. /// - /// Errors if there are any undeclared labels. - fn backpatch_and_emit(&mut self) -> Result, Error> { - if !self.undeclared_labels.is_empty() { - return error::UndeclaredLabels { - labels: self - .undeclared_labels - .iter() - .map(|l| l.to_owned()) - .collect::>(), - } - .fail(); - } - let mut output = Vec::new(); - for op in self.ready.iter() { - if let RawOp::Op(ref op) = op { - match op - .clone() - .concretize((&self.declared_labels, &self.declared_macros).into()) - { - Ok(mut cop) => { - if let AbstractOp::Push(imm) = op { - let exp = imm.tree.eval_with_context( - (&self.declared_labels, &self.declared_macros).into(), - ); - - let labels: HashSet = imm - .tree - .labels(&self.declared_macros) - .unwrap() - .into_iter() - .collect(); - if !labels.is_empty() { - if let Ok(val) = exp { - let extra = val.to_usize().unwrap() / 256; - if extra > 0 { - self.concrete_len += extra; - - for label in labels { - if let Some(position) = - self.declared_labels.get_mut(&label) - { - *position = position.map(|v| v + 1); - } - } - } - } - } - - cop = op - .clone() - .concretize((&self.declared_labels, &self.declared_macros).into()) - .unwrap(); - } - cop.assemble(&mut output) - } - Err(ops::Error::ContextIncomplete { - source: UnknownLabel { .. }, - }) => { - return error::UndeclaredLabels { - labels: self.undeclared_labels.to_vec(), - } - .fail(); - } - Err(ops::Error::ContextIncomplete { - source: UnknownMacro { name, .. }, - }) => { - return error::UndeclaredInstructionMacro { name }.fail(); - } - Err(ops::Error::ContextIncomplete { - source: UndefinedVariable { name, .. }, - }) => { - return error::UndeclaredVariableMacro { var: name }.fail(); - } + /// Returns the code of the assembled program. + pub fn assemble(&mut self, ops: &[O]) -> Result, Error> + where + O: Into + Clone, + { + self.declare_macros(ops)?; - Err(_) => unreachable!("all ops should be concretizable"), - } - } else if let RawOp::Raw(raw) = op { - output.extend(raw); - } + for op in ops { + self.push(op.clone().into())?; } + let output = self.backpatch_and_emit()?; + self.ready.clear(); Ok(output) } @@ -333,24 +265,6 @@ impl Assembler { Ok(()) } - /// Feed instructions into the `Assembler`. - /// - /// Returns the code of the assembled program. - pub fn assemble(&mut self, ops: &[O]) -> Result, Error> - where - O: Into + Clone, - { - self.declare_macros(ops)?; - - for op in ops { - self.push(op.clone().into())?; - } - - let output = self.backpatch_and_emit()?; - self.ready.clear(); - Ok(output) - } - /// Feed a single instruction into the `Assembler`. fn push(&mut self, rop: O) -> Result where @@ -432,6 +346,92 @@ impl Assembler { Ok(self.concrete_len) } + /// Backpatch dynamic operations and emit the assembled program. + /// + /// Errors if there are any undeclared labels. + fn backpatch_and_emit(&mut self) -> Result, Error> { + if !self.undeclared_labels.is_empty() { + return error::UndeclaredLabels { + labels: self + .undeclared_labels + .iter() + .map(|l| l.to_owned()) + .collect::>(), + } + .fail(); + } + let mut output = Vec::new(); + for op in self.ready.iter() { + if let RawOp::Op(ref op) = op { + match op + .clone() + .concretize((&self.declared_labels, &self.declared_macros).into()) + { + Ok(mut cop) => { + if let AbstractOp::Push(imm) = op { + let exp = imm.tree.eval_with_context( + (&self.declared_labels, &self.declared_macros).into(), + ); + + let labels: HashSet = imm + .tree + .labels(&self.declared_macros) + .unwrap() + .into_iter() + .collect(); + if !labels.is_empty() { + if let Ok(val) = exp { + let extra = val.to_usize().unwrap() / 256; + if extra > 0 { + self.concrete_len += extra; + + for label in labels { + if let Some(position) = + self.declared_labels.get_mut(&label) + { + *position = position.map(|v| v + 1); + } + } + } + } + } + + cop = op + .clone() + .concretize((&self.declared_labels, &self.declared_macros).into()) + .unwrap(); + } + cop.assemble(&mut output) + } + Err(ops::Error::ContextIncomplete { + source: UnknownLabel { .. }, + }) => { + return error::UndeclaredLabels { + labels: self.undeclared_labels.to_vec(), + } + .fail(); + } + Err(ops::Error::ContextIncomplete { + source: UnknownMacro { name, .. }, + }) => { + return error::UndeclaredInstructionMacro { name }.fail(); + } + Err(ops::Error::ContextIncomplete { + source: UndefinedVariable { name, .. }, + }) => { + return error::UndeclaredVariableMacro { var: name }.fail(); + } + + Err(_) => unreachable!("all ops should be concretizable"), + } + } else if let RawOp::Raw(raw) = op { + output.extend(raw); + } + } + + Ok(output) + } + fn declare_label(&mut self, rop: &RawOp) -> Result<(), Error> { if let RawOp::Op(AbstractOp::Label(label)) = rop { if self.declared_labels.contains_key(label) { From 0d2a2a7e47f8e760f6a11f3e1fe88b8a8075bef8 Mon Sep 17 00:00:00 2001 From: Gaston Zanitti Date: Fri, 10 Nov 2023 11:35:02 -0300 Subject: [PATCH 66/73] Minor changes to merge backend simplification --- Cargo.lock | 5 +- etk-asm/Cargo.toml | 17 +- etk-asm/src/asm.rs | 484 ++++++++++++++++++++---------------------- etk-asm/src/ingest.rs | 2 +- 4 files changed, 249 insertions(+), 259 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c642e060..cad59fe3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -440,6 +440,7 @@ dependencies = [ "hex", "hex-literal", "num-bigint", + "num-traits", "pest", "pest_derive", "rand", @@ -937,9 +938,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" dependencies = [ "autocfg", ] diff --git a/etk-asm/Cargo.toml b/etk-asm/Cargo.toml index a479bd3e..e2b4622d 100644 --- a/etk-asm/Cargo.toml +++ b/etk-asm/Cargo.toml @@ -1,7 +1,10 @@ [package] name = "etk-asm" version = "0.4.0-dev" -authors = ["Sam Wilson ", "lightclient "] +authors = [ + "Sam Wilson ", + "lightclient ", +] license = "MIT OR Apache-2.0" edition = "2018" description = "EVM Toolkit assembler" @@ -9,23 +12,29 @@ homepage = "https://quilt.github.io/etk" repository = "https://github.com/quilt/etk" readme = "README.md" keywords = ["etk", "ethereum", "assembler"] -categories = ["cryptography::cryptocurrencies", "command-line-utilities", "development-tools", "compilers"] +categories = [ + "cryptography::cryptocurrencies", + "command-line-utilities", + "development-tools", + "compilers", +] [features] cli = ["clap", "etk-cli"] -backtraces = [ "snafu/backtraces", "etk-ops/backtraces" ] +backtraces = ["snafu/backtraces", "etk-ops/backtraces"] [dependencies] etk-ops = { path = "../etk-ops", version = "0.4.0-dev" } etk-cli = { optional = true, path = "../etk-cli", version = "0.4.0-dev" } hex = "0.4.3" num-bigint = "0.4" +num-traits = "0.2.17" pest = "2.1.3" pest_derive = "2.1" rand = "0.8.5" sha3 = "0.10.1" clap = { optional = true, version = "3.1", features = ["derive"] } -snafu = { version = "0.7.1", default-features = false, features = [ "std" ] } +snafu = { version = "0.7.1", default-features = false, features = ["std"] } [dev-dependencies] assert_matches = "1.5.0" diff --git a/etk-asm/src/asm.rs b/etk-asm/src/asm.rs index 4ff95fb7..30aafc28 100644 --- a/etk-asm/src/asm.rs +++ b/etk-asm/src/asm.rs @@ -144,9 +144,9 @@ pub use self::error::Error; use crate::ops::expression::Error::{UndefinedVariable, UnknownLabel, UnknownMacro}; use crate::ops::{self, AbstractOp, Assemble, Expression, MacroDefinition}; use etk_ops::HardFork; +use num_traits::cast::ToPrimitive; use rand::Rng; -use std::cmp; -use std::collections::{hash_map, HashMap}; +use std::collections::{hash_map, HashMap, HashSet}; /// An item to be assembled, which can be either an [`AbstractOp`], /// the inclusion of a new scope or a raw byte sequence. @@ -211,25 +211,12 @@ pub struct Assembler { /// Labels that have been referred to (ex. with push) but /// have not been declared with an `AbstractOp::Label`. - undeclared_labels: Vec, + undeclared_labels: Vec, /// Hardfork to use when assembling. hardfork: HardFork, } -/// Struct used to keep track of pending label invocations and their positions in code. -#[derive(Debug, Clone)] -struct PendingLabel { - /// The name of the label. - label: String, - - /// Position where the label was invoked. - position: usize, - - /// Whether the label was invoked with a dynamic push or not. - dynamic_push: bool, -} - impl Assembler { /// Create a new `Assembler` for the last hardfork. pub fn new() -> Self { @@ -244,112 +231,46 @@ impl Assembler { } } - /// Inspect the macros in a series of [`RawOp`] and declare them in the assembler. - fn inspect_macros(&mut self, nodes: &I) -> Result<(), Error> + /// Feed instructions into the `Assembler`. + /// + /// Returns the code of the assembled program. + pub fn assemble(&mut self, ops: &[O]) -> Result, Error> where - I: IntoIterator + Clone, - O: Into, + O: Into + Clone, { - for op in nodes.clone() { - let op = op.into(); - if let RawOp::Op(AbstractOp::MacroDefinition(_)) = op { - self.declare_macro(op)? - } - } - - Ok(()) - } - - /// Collect any assembled instructions that are ready to be output. - fn take(&mut self) -> Vec { - let output = self.concretize_ops(); - match output { - Ok(v) => { - self.ready.clear(); - v - } - Err(_) => Vec::new(), - } - } + self.declare_macros(ops)?; - /// Concretize all assembled instructions. - fn concretize_ops(&mut self) -> Result, Error> { - let mut output = Vec::new(); - for op in self.ready.iter() { - if let RawOp::Op(ref op) = op { - match op.clone().concretize( - (&self.declared_labels, &self.declared_macros).into(), - self.hardfork.clone(), - ) { - Ok(cop) => cop.assemble(&mut output), - Err(ops::Error::ContextIncomplete { - source: UnknownLabel { label: _label, .. }, - }) => { - let undeclared_names: Vec<_> = self - .undeclared_labels - .iter() - .map(|PendingLabel { label, .. }| label.clone()) - .collect(); - return error::UndeclaredLabels { - labels: undeclared_names, - } - .fail(); - } - Err(ops::Error::ContextIncomplete { - source: UnknownMacro { name, .. }, - }) => { - return error::UndeclaredInstructionMacro { name }.fail(); - } - Err(ops::Error::ContextIncomplete { - source: UndefinedVariable { name, .. }, - }) => { - return error::UndeclaredVariableMacro { var: name }.fail(); - } - - Err(_) => unreachable!("all ops should be concretizable"), - } - } else if let RawOp::Raw(raw) = op { - output.extend(raw); - } + for op in ops { + self.push(op.clone().into())?; } + let output = self.backpatch_and_emit()?; + self.ready.clear(); Ok(output) } - /// Check if the input sequence is complete. Returns any errors that - /// may remain. - fn finish(&mut self) -> Result<(), Error> { - if !self.undeclared_labels.is_empty() { - return error::UndeclaredLabels { - labels: self - .undeclared_labels - .iter() - .map(|l| l.label.to_owned()) - .collect::>(), - } - .fail(); - } - - Ok(()) - } - - /// Feed instructions into the `Assembler`. + /// Pre-define macros, via `AbstractOp`, into the `Assembler`. /// - /// Returns the code of the assembled program. - pub fn assemble(&mut self, ops: I) -> Result, Error> + /// This is used to define macros that are used in the same scope. + fn declare_macros(&mut self, ops: &[O]) -> Result<(), Error> where - I: IntoIterator + Clone, - O: Into, + O: Into + Clone, { - self.inspect_macros(&ops)?; - for op in ops { - self.push(op)?; + let rop = op.clone().into(); + if let RawOp::Op(AbstractOp::MacroDefinition(ref defn)) = rop { + match self.declared_macros.entry(defn.name().to_owned()) { + hash_map::Entry::Occupied(_) => { + return error::DuplicateMacro { name: defn.name() }.fail() + } + hash_map::Entry::Vacant(v) => { + v.insert(defn.to_owned()); + } + } + } } - self.finish()?; - - Ok(self.take()) + Ok(()) } /// Insert explicitly declared macros, via `AbstractOp`, into the `Assembler`. @@ -378,63 +299,22 @@ impl Assembler { O: Into, { let rop = rop.into(); - self.declare_label(&rop)?; - // Expand instruction macros immediately. - if let RawOp::Op(AbstractOp::Macro(ref m)) = rop { - self.expand_macro(&m.name, &m.parameters)?; - return Ok(self.concrete_len); - } - - self.push_rawop(rop)?; - Ok(self.concrete_len) - } - - fn declare_label(&mut self, rop: &RawOp) -> Result<(), Error> { - if let RawOp::Op(AbstractOp::Label(ref label)) = *rop { - match self.declared_labels.entry(label.to_owned()) { - hash_map::Entry::Occupied(_) => { - return error::DuplicateLabel { label }.fail(); - } - hash_map::Entry::Vacant(v) => { - v.insert(None); - } - } - } - Ok(()) - } - - fn push_rawop(&mut self, rop: RawOp) -> Result<(), Error> { match rop { RawOp::Op(AbstractOp::Label(label)) => { - let mut dst = 0; - for ul in self.undeclared_labels.iter() { - if (ul.label == label) & ul.dynamic_push { - // Compensation in case label was dynamically pushed. - let mut tmp = - ((self.concrete_len as f32 - ul.position as f32) / 256.0).floor(); - - // Size already accounted for %push(label) was 2. - if tmp > 1.0 { - tmp -= 1.0; - } - dst = cmp::max(tmp as usize, dst); - } - } - - self.undeclared_labels.retain(|l| l.label != *label); - self.concrete_len += dst; + self.undeclared_labels.retain(|l| *l != label); let old = self .declared_labels .insert(label, Some(self.concrete_len)) .expect("label should exist"); assert_eq!(old, None, "label should have been undefined"); - Ok(()) } - RawOp::Op(AbstractOp::MacroDefinition(_)) => Ok(()), - RawOp::Op(AbstractOp::Macro(_)) => Ok(()), + RawOp::Op(AbstractOp::MacroDefinition(_)) => {} + RawOp::Op(AbstractOp::Macro(ref m)) => { + self.expand_macro(&m.name, &m.parameters)?; + } RawOp::Op(ref op) => { match op.clone().concretize( (&self.declared_labels, &self.declared_macros).into(), @@ -442,7 +322,7 @@ impl Assembler { ) { Ok(cop) => { self.concrete_len += cop.size(); - self.ready.push(rop) + self.ready.push(rop.clone()) } Err(ops::Error::ExpressionTooLarge { value, spec, .. }) => { return error::ExpressionTooLarge { @@ -460,23 +340,16 @@ impl Assembler { .fail() } Err(ops::Error::ContextIncomplete { - source: UnknownLabel { label: _label, .. }, + source: UnknownLabel { label, .. }, }) => { - let mut dynamic_push = false; - match op.size() { - Some(size) => self.concrete_len += size, - None => { - self.concrete_len += 2; - dynamic_push = true; - } - }; - - self.undeclared_labels.push(PendingLabel { - label: _label.to_owned(), - position: self.ready.len(), - dynamic_push, - }); - self.ready.push(rop); + if let AbstractOp::Push(_) = op { + self.concrete_len += 2; + } else { + self.concrete_len += op.size().unwrap(); + } + + self.undeclared_labels.push(label); + self.ready.push(rop.clone()); } Err(ops::Error::ContextIncomplete { source: UnknownMacro { name, .. }, @@ -485,22 +358,122 @@ impl Assembler { source: UndefinedVariable { name, .. }, }) => return error::UndeclaredVariableMacro { var: name }.fail(), } - - Ok(()) } RawOp::Raw(raw) => { self.concrete_len += raw.len(); - self.ready.push(RawOp::Raw(raw)); - Ok(()) + self.ready.push(RawOp::Raw(raw.to_vec())); } RawOp::Scope(scope) => { let mut asm = Self::new(); - let scope_result = asm.assemble(scope)?; + let scope_result = asm.assemble(&scope)?; self.concrete_len += scope_result.len(); self.ready.push(RawOp::Raw(scope_result)); - Ok(()) } } + + Ok(self.concrete_len) + } + + /// Backpatch dynamic operations and emit the assembled program. + /// + /// Errors if there are any undeclared labels. + fn backpatch_and_emit(&mut self) -> Result, Error> { + if !self.undeclared_labels.is_empty() { + return error::UndeclaredLabels { + labels: self + .undeclared_labels + .iter() + .map(|l| l.to_owned()) + .collect::>(), + } + .fail(); + } + let mut output = Vec::new(); + for op in self.ready.iter() { + if let RawOp::Op(ref op) = op { + match op.clone().concretize( + (&self.declared_labels, &self.declared_macros).into(), + self.hardfork.clone(), + ) { + Ok(mut cop) => { + if let AbstractOp::Push(imm) = op { + let exp = imm.tree.eval_with_context( + (&self.declared_labels, &self.declared_macros).into(), + ); + + let labels: HashSet = imm + .tree + .labels(&self.declared_macros) + .unwrap() + .into_iter() + .collect(); + if !labels.is_empty() { + if let Ok(val) = exp { + let extra = val.to_usize().unwrap() / 256; + if extra > 0 { + self.concrete_len += extra; + + for label in labels { + if let Some(position) = + self.declared_labels.get_mut(&label) + { + *position = position.map(|v| v + 1); + } + } + } + } + } + + cop = op + .clone() + .concretize( + (&self.declared_labels, &self.declared_macros).into(), + self.hardfork.clone(), + ) + .unwrap(); + } + cop.assemble(&mut output) + } + Err(ops::Error::ContextIncomplete { + source: UnknownLabel { .. }, + }) => { + return error::UndeclaredLabels { + labels: self.undeclared_labels.to_vec(), + } + .fail(); + } + Err(ops::Error::ContextIncomplete { + source: UnknownMacro { name, .. }, + }) => { + return error::UndeclaredInstructionMacro { name }.fail(); + } + Err(ops::Error::ContextIncomplete { + source: UndefinedVariable { name, .. }, + }) => { + return error::UndeclaredVariableMacro { var: name }.fail(); + } + + Err(_) => unreachable!("all ops should be concretizable"), + } + } else if let RawOp::Raw(raw) = op { + output.extend(raw); + } + } + + Ok(output) + } + + fn declare_label(&mut self, rop: &RawOp) -> Result<(), Error> { + if let RawOp::Op(AbstractOp::Label(label)) = rop { + if self.declared_labels.contains_key(label) { + return error::DuplicateLabel { + label: label.to_owned(), + } + .fail(); + } + self.declared_labels.insert(label.to_owned(), None); + } + Ok(()) } fn expand_macro( @@ -585,11 +558,12 @@ mod tests { #[test] fn assemble_variable_push_const_while_pending() -> Result<(), Error> { let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); - let result = asm.assemble(vec![ + let ops = vec![ AbstractOp::Op(HardForkOp::Cancun(Push1(Imm::with_label("label1")).into())), AbstractOp::Push(Terminal::Number(0xaabb.into()).into()), AbstractOp::Label("label1".into()), - ])?; + ]; + let result = asm.assemble(&ops)?; assert_eq!(result, hex!("600561aabb")); Ok(()) } @@ -597,7 +571,7 @@ mod tests { #[test] fn assemble_variable_pushes_abab() -> Result<(), Error> { let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); - let result = asm.assemble(vec![ + let ops = vec![ AbstractOp::new(HardForkOp::Cancun(JumpDest.into())), AbstractOp::Push(Imm::with_label("label1")), AbstractOp::Push(Imm::with_label("label2")), @@ -605,7 +579,8 @@ mod tests { AbstractOp::new(HardForkOp::Cancun(GetPc.into())), AbstractOp::Label("label2".into()), AbstractOp::new(HardForkOp::Cancun(GetPc.into())), - ])?; + ]; + let result = asm.assemble(&ops)?; assert_eq!(result, hex!("5b600560065858")); Ok(()) } @@ -613,7 +588,7 @@ mod tests { #[test] fn assemble_variable_pushes_abba() -> Result<(), Error> { let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); - let result = asm.assemble(vec![ + let ops = vec![ AbstractOp::new(HardForkOp::Cancun(JumpDest.into())), AbstractOp::Push(Imm::with_label("label1")), AbstractOp::Push(Imm::with_label("label2")), @@ -621,7 +596,8 @@ mod tests { AbstractOp::new(HardForkOp::Cancun(GetPc.into())), AbstractOp::Label("label1".into()), AbstractOp::new(HardForkOp::Cancun(GetPc.into())), - ])?; + ]; + let result = asm.assemble(&ops)?; assert_eq!(result, hex!("5b600660055858")); Ok(()) } @@ -629,12 +605,13 @@ mod tests { #[test] fn assemble_variable_push1_multiple() -> Result<(), Error> { let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); - let result = asm.assemble(vec![ + let ops = vec![ AbstractOp::new(HardForkOp::Cancun(JumpDest.into())), AbstractOp::Push(Imm::with_label("auto")), AbstractOp::Push(Imm::with_label("auto")), AbstractOp::Label("auto".into()), - ])?; + ]; + let result = asm.assemble(&ops)?; assert_eq!(result, hex!("5b60056005")); Ok(()) } @@ -642,9 +619,10 @@ mod tests { #[test] fn assemble_variable_push_const() -> Result<(), Error> { let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); - let result = asm.assemble(vec![AbstractOp::Push( + let ops = vec![AbstractOp::Push( Terminal::Number((0x00aaaaaaaaaaaaaaaaaaaaaaaa as u128).into()).into(), - )])?; + )]; + let result = asm.assemble(&ops)?; assert_eq!(result, hex!("6baaaaaaaaaaaaaaaaaaaaaaaa")); Ok(()) } @@ -654,9 +632,8 @@ mod tests { let v = BigInt::from_bytes_be(Sign::Plus, &[1u8; 33]); let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); - let err = asm - .assemble(vec![AbstractOp::Push(Terminal::Number(v).into())]) - .unwrap_err(); + let ops = vec![AbstractOp::Push(Terminal::Number(v).into())]; + let err = asm.assemble(&ops).unwrap_err(); assert_matches!(err, Error::ExpressionTooLarge { .. }); } @@ -664,9 +641,8 @@ mod tests { #[test] fn assemble_variable_push_negative() { let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); - let err = asm - .assemble(vec![AbstractOp::Push(Terminal::Number((-1).into()).into())]) - .unwrap_err(); + let ops = vec![AbstractOp::Push(Terminal::Number((-1).into()).into())]; + let err = asm.assemble(&ops).unwrap_err(); assert_matches!(err, Error::ExpressionNegative { .. }); } @@ -674,9 +650,10 @@ mod tests { #[test] fn assemble_variable_push_const0() -> Result<(), Error> { let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); - let result = asm.assemble(vec![AbstractOp::Push( + let ops = vec![AbstractOp::Push( Terminal::Number((0x00 as u128).into()).into(), - )])?; + )]; + let result = asm.assemble(&ops)?; assert_eq!(result, hex!("6000")); Ok(()) } @@ -684,11 +661,12 @@ mod tests { #[test] fn assemble_variable_push1_known() -> Result<(), Error> { let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); - let result = asm.assemble(vec![ + let ops = vec![ AbstractOp::new(HardForkOp::Cancun(JumpDest.into())), AbstractOp::Label("auto".into()), AbstractOp::Push(Imm::with_label("auto")), - ])?; + ]; + let result = asm.assemble(&ops)?; assert_eq!(result, hex!("5b6001")); Ok(()) } @@ -696,11 +674,12 @@ mod tests { #[test] fn assemble_variable_push1() -> Result<(), Error> { let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); - let result = asm.assemble(vec![ + let ops = vec![ AbstractOp::Push(Imm::with_label("auto")), AbstractOp::Label("auto".into()), AbstractOp::new(HardForkOp::Cancun(JumpDest.into())), - ])?; + ]; + let result = asm.assemble(&ops)?; assert_eq!(result, hex!("60025b")); Ok(()) } @@ -708,12 +687,13 @@ mod tests { #[test] fn assemble_variable_push1_reuse() -> Result<(), Error> { let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); - let result = asm.assemble(vec![ + let ops = vec![ AbstractOp::Push(Imm::with_label("auto")), AbstractOp::Label("auto".into()), AbstractOp::new(HardForkOp::Cancun(JumpDest.into())), AbstractOp::new(HardForkOp::Cancun(Push1(Imm::with_label("auto")).into())), - ])?; + ]; + let result = asm.assemble(&ops)?; assert_eq!(result, hex!("60025b6002")); Ok(()) } @@ -730,7 +710,7 @@ mod tests { code.push(AbstractOp::new(HardForkOp::Cancun(JumpDest.into()))); let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); - let result = asm.assemble(code)?; + let result = asm.assemble(&code)?; let mut expected = vec![0x61, 0x01, 0x02]; expected.extend_from_slice(&[0x58; 255]); @@ -743,11 +723,10 @@ mod tests { #[test] fn assemble_undeclared_label() -> Result<(), Error> { let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); - let err = asm - .assemble(vec![AbstractOp::new(HardForkOp::Cancun( - Push1(Imm::with_label("hi")).into(), - ))]) - .unwrap_err(); + let ops = vec![AbstractOp::new(HardForkOp::Cancun( + Push1(Imm::with_label("hi")).into(), + ))]; + let err = asm.assemble(&ops).unwrap_err(); assert_matches!(err, Error::UndeclaredLabels { labels, .. } if labels == vec!["hi"]); Ok(()) } @@ -755,7 +734,8 @@ mod tests { #[test] fn assemble_jumpdest_no_label() -> Result<(), Error> { let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); - let result = asm.assemble(vec![AbstractOp::new(HardForkOp::Cancun(JumpDest.into()))])?; + let ops = vec![AbstractOp::new(HardForkOp::Cancun(JumpDest.into()))]; + let result = asm.assemble(&ops)?; assert!(asm.declared_labels.is_empty()); assert_eq!(result, hex!("5b")); Ok(()) @@ -769,7 +749,7 @@ mod tests { AbstractOp::new(HardForkOp::Cancun(JumpDest.into())), ]; - let result = asm.assemble(ops)?; + let result = asm.assemble(&ops)?; assert_eq!(asm.declared_labels.len(), 1); assert_eq!(asm.declared_labels.get("lbl"), Some(&Some(0))); assert_eq!(result, hex!("5b")); @@ -785,7 +765,7 @@ mod tests { ]; let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); - let result = asm.assemble(ops)?; + let result = asm.assemble(&ops)?; assert_eq!(result, hex!("5b6000")); Ok(()) @@ -800,7 +780,7 @@ mod tests { ]; let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); - let result = asm.assemble(ops)?; + let result = asm.assemble(&ops)?; assert_eq!(result, hex!("600258")); Ok(()) @@ -815,7 +795,7 @@ mod tests { ]; let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); - let result = asm.assemble(ops)?; + let result = asm.assemble(&ops)?; assert_eq!(result, hex!("60025b")); Ok(()) @@ -832,7 +812,7 @@ mod tests { Push1(Imm::with_label("a")).into(), ))); let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); - let err = asm.assemble(ops).unwrap_err(); + let err = asm.assemble(&ops).unwrap_err(); assert_matches!(err, Error::ExpressionTooLarge { expr: Expression::Terminal(Terminal::Label(label)), .. } if label == "a"); } @@ -847,7 +827,7 @@ mod tests { Push1(Imm::with_label("b")).into(), ))); let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); - let result = asm.assemble(ops)?; + let result = asm.assemble(&ops)?; let mut expected = vec![0x58; 255]; expected.push(0x5b); @@ -886,7 +866,7 @@ mod tests { ]; let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); - let result = asm.assemble(ops)?; + let result = asm.assemble(&ops)?; assert_eq!(result, []); Ok(()) @@ -920,7 +900,7 @@ mod tests { ]; let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); - let result = asm.assemble(ops)?; + let result = asm.assemble(&ops)?; assert_eq!(result, hex!("5b60005b600360005b60086000")); Ok(()) @@ -950,7 +930,7 @@ mod tests { ]; let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); - let result = asm.assemble(ops)?; + let result = asm.assemble(&ops)?; assert_eq!(result, hex!("5b60005b60036000")); Ok(()) @@ -980,7 +960,7 @@ mod tests { ]; let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); - let result = asm.assemble(ops)?; + let result = asm.assemble(&ops)?; assert_eq!(result, hex!("5b60005b60036000")); Ok(()) @@ -1010,7 +990,7 @@ mod tests { ]; let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); - let result = asm.assemble(ops)?; + let result = asm.assemble(&ops)?; assert_eq!(result, hex!("5b600560065858")); Ok(()) @@ -1022,7 +1002,7 @@ mod tests { InstructionMacroInvocation::with_zero_parameters("my_macro".into()), )]; let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); - let err = asm.assemble(ops).unwrap_err(); + let err = asm.assemble(&ops).unwrap_err(); assert_matches!(err, Error::UndeclaredInstructionMacro { name, .. } if name == "my_macro"); Ok(()) @@ -1045,7 +1025,7 @@ mod tests { .into(), ]; let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); - let err = asm.assemble(ops).unwrap_err(); + let err = asm.assemble(&ops).unwrap_err(); assert_matches!(err, Error::DuplicateMacro { name, .. } if name == "my_macro"); Ok(()) @@ -1065,7 +1045,7 @@ mod tests { )), ]; let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); - let err = asm.assemble(ops).unwrap_err(); + let err = asm.assemble(&ops).unwrap_err(); assert_matches!(err, Error::DuplicateLabel { label, .. } if label == "a"); Ok(()) @@ -1092,7 +1072,7 @@ mod tests { AbstractOp::new(HardForkOp::Cancun(Push1(Imm::with_label("a")).into())), ]; let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); - let result = asm.assemble(ops)?; + let result = asm.assemble(&ops)?; assert_eq!(result, hex!("3360016000")); @@ -1124,7 +1104,7 @@ mod tests { ]; let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); - let result = asm.assemble(ops)?; + let result = asm.assemble(&ops)?; assert_eq!(result, hex!("5b600060426000")); Ok(()) @@ -1137,7 +1117,7 @@ mod tests { ))]; let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); - let result = asm.assemble(ops)?; + let result = asm.assemble(&ops)?; assert_eq!(result, hex!("6002")); Ok(()) @@ -1149,7 +1129,7 @@ mod tests { Push1(Imm::with_expression(BigInt::from(-1).into())).into(), ))]; let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); - let err = asm.assemble(ops).unwrap_err(); + let err = asm.assemble(&ops).unwrap_err(); assert_matches!(err, Error::ExpressionNegative { value, .. } if value == BigInt::from(-1)); Ok(()) @@ -1158,14 +1138,13 @@ mod tests { #[test] fn assemble_expression_undeclared_label() -> Result<(), Error> { let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); - let err = asm - .assemble(vec![AbstractOp::new(HardForkOp::Cancun( - Push1(Imm::with_expression( - Terminal::Label(String::from("hi")).into(), - )) - .into(), - ))]) - .unwrap_err(); + let ops = vec![AbstractOp::new(HardForkOp::Cancun( + Push1(Imm::with_expression( + Terminal::Label(String::from("hi")).into(), + )) + .into(), + ))]; + let err = asm.assemble(&ops).unwrap_err(); assert_matches!(err, Error::UndeclaredLabels { labels, .. } if labels == vec!["hi"]); Ok(()) } @@ -1173,16 +1152,15 @@ mod tests { #[test] fn assemble_variable_push_expression_with_undeclared_labels() -> Result<(), Error> { let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); - let err = asm - .assemble(vec![ - AbstractOp::new(HardForkOp::Cancun(JumpDest.into())), - AbstractOp::Push(Imm::with_expression(Expression::Plus( - Terminal::Label("foo".into()).into(), - Terminal::Label("bar".into()).into(), - ))), - AbstractOp::new(HardForkOp::Cancun(Gas.into())), - ]) - .unwrap_err(); + let ops = vec![ + AbstractOp::new(HardForkOp::Cancun(JumpDest.into())), + AbstractOp::Push(Imm::with_expression(Expression::Plus( + Terminal::Label("foo".into()).into(), + Terminal::Label("bar".into()).into(), + ))), + AbstractOp::new(HardForkOp::Cancun(Gas.into())), + ]; + let err = asm.assemble(&ops).unwrap_err(); // The expressions have short-circuit evaluation, so only the first label is caught in the error. assert_matches!(err, Error::UndeclaredLabels { labels, .. } if (labels.contains(&"foo".to_string()))); Ok(()) @@ -1191,14 +1169,15 @@ mod tests { #[test] fn assemble_variable_push1_expression() -> Result<(), Error> { let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); - let result = asm.assemble(vec![ + let ops = vec![ AbstractOp::new(HardForkOp::Cancun(JumpDest.into())), AbstractOp::Label("auto".into()), AbstractOp::Push(Imm::with_expression(Expression::Plus( 1.into(), Terminal::Label(String::from("auto")).into(), ))), - ])?; + ]; + let result = asm.assemble(&ops)?; assert_eq!(result, hex!("5b6002")); Ok(()) } @@ -1206,7 +1185,7 @@ mod tests { #[test] fn assemble_expression_with_labels() -> Result<(), Error> { let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); - let result = asm.assemble(vec![ + let ops = vec![ AbstractOp::new(HardForkOp::Cancun(JumpDest.into())), AbstractOp::Push(Imm::with_expression(Expression::Plus( Terminal::Label(String::from("foo")).into(), @@ -1215,7 +1194,8 @@ mod tests { AbstractOp::new(HardForkOp::Cancun(Gas.into())), AbstractOp::Label("foo".into()), AbstractOp::Label("bar".into()), - ])?; + ]; + let result = asm.assemble(&ops)?; assert_eq!(result, hex!("5b60085a")); Ok(()) } @@ -1239,7 +1219,7 @@ mod tests { ]; let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); - let result = asm.assemble(ops)?; + let result = asm.assemble(&ops)?; assert_eq!(result, hex!("6002")); Ok(()) @@ -1266,7 +1246,7 @@ mod tests { ]; let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); - let err = asm.assemble(ops).unwrap_err(); + let err = asm.assemble(&ops).unwrap_err(); assert_matches!(err, Error::UndeclaredVariableMacro { var, .. } if var == "bar"); } @@ -1298,7 +1278,7 @@ mod tests { ]; let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); - let result = asm.assemble(ops)?; + let result = asm.assemble(&ops)?; assert_eq!(result, hex!("58335b")); Ok(()) @@ -1331,7 +1311,7 @@ mod tests { ]; let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); - let result = asm.assemble(ops)?; + let result = asm.assemble(&ops)?; assert_eq!(result, hex!("585b33")); Ok(()) diff --git a/etk-asm/src/ingest.rs b/etk-asm/src/ingest.rs index 8e3b33e8..67125aca 100644 --- a/etk-asm/src/ingest.rs +++ b/etk-asm/src/ingest.rs @@ -317,7 +317,7 @@ where let mut program = Program::new(path.into()); let nodes = self.preprocess(&mut program, text)?; let mut asm = Assembler::new(); - let raw = asm.assemble(nodes)?; + let raw = asm.assemble(&nodes)?; self.output.write_all(&raw).context(error::Io { message: "writing output", From e8ffd1fff41dec90dcd77508c43ffdfd2372c73f Mon Sep 17 00:00:00 2001 From: Gaston Zanitti Date: Fri, 10 Nov 2023 11:35:57 -0300 Subject: [PATCH 67/73] Minor changes to merge backend simplification --- etk-asm/src/asm.rs | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/etk-asm/src/asm.rs b/etk-asm/src/asm.rs index 30aafc28..9db633ac 100644 --- a/etk-asm/src/asm.rs +++ b/etk-asm/src/asm.rs @@ -273,26 +273,6 @@ impl Assembler { Ok(()) } - /// Insert explicitly declared macros, via `AbstractOp`, into the `Assembler`. - fn declare_macro(&mut self, rop: O) -> Result<(), Error> - where - O: Into, - { - let rop = rop.into(); - if let RawOp::Op(AbstractOp::MacroDefinition(ref defn)) = rop { - match self.declared_macros.entry(defn.name().to_owned()) { - hash_map::Entry::Occupied(_) => { - return error::DuplicateMacro { name: defn.name() }.fail() - } - hash_map::Entry::Vacant(v) => { - v.insert(defn.to_owned()); - } - } - } - - Ok(()) - } - /// Feed a single instruction into the `Assembler`. fn push(&mut self, rop: O) -> Result where From 91f318b90e66b765b827a16bce46d6c7cb928db4 Mon Sep 17 00:00:00 2001 From: Gaston Zanitti Date: Fri, 10 Nov 2023 11:47:34 -0300 Subject: [PATCH 68/73] Updated to backend simplification (last version) --- etk-asm/tests/asm.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/etk-asm/tests/asm.rs b/etk-asm/tests/asm.rs index 06bcbbf9..43f68532 100644 --- a/etk-asm/tests/asm.rs +++ b/etk-asm/tests/asm.rs @@ -112,11 +112,7 @@ fn instruction_macro_with_two_instructions_per_line() { #[test] fn undefined_label_undefined_macro() { let mut output = Vec::new(); -<<<<<<< HEAD let mut ingester = Ingest::new(&mut output, HardFork::Cancun); -======= - let mut ingester = Ingest::new(&mut output); ->>>>>>> pending_state_removal let err = ingester .ingest_file(source(&[ "instruction-macro", From 9a5ff95d5c1208cd1b41dfb67398a8fa3346e3a2 Mon Sep 17 00:00:00 2001 From: Gaston Zanitti Date: Fri, 10 Nov 2023 11:48:25 -0300 Subject: [PATCH 69/73] Updated to backend simplification (last version) --- etk-asm/tests/asm.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/etk-asm/tests/asm.rs b/etk-asm/tests/asm.rs index 43f68532..da0e1c57 100644 --- a/etk-asm/tests/asm.rs +++ b/etk-asm/tests/asm.rs @@ -297,7 +297,7 @@ fn every_op() -> Result<(), Error> { #[test] fn test_dynamic_push_and_include() -> Result<(), Error> { let mut output = Vec::new(); - let mut ingester = Ingest::new(&mut output); + let mut ingester = Ingest::new(&mut output, HardFork::Cancun); ingester.ingest_file(source(&["variable-push", "main.etk"]))?; let tmp: Vec = output.iter().map(|&num| format!("{:x?}", num)).collect(); @@ -350,17 +350,17 @@ fn test_invalid_range_hardfork() { } fn test_dynamic_push2() -> Result<(), Error> { let mut output = Vec::new(); - let mut ingester = Ingest::new(&mut output); + let mut ingester = Ingest::new(&mut output, HardFork::Cancun); ingester.ingest_file(source(&["variable-push2", "main1.etk"]))?; assert_eq!(output, hex!("61010158")); let mut output = Vec::new(); - let mut ingester = Ingest::new(&mut output); + let mut ingester = Ingest::new(&mut output, HardFork::Cancun); ingester.ingest_file(source(&["variable-push2", "main2.etk"]))?; assert_eq!(output, hex!("61010158")); let mut output = Vec::new(); - let mut ingester = Ingest::new(&mut output); + let mut ingester = Ingest::new(&mut output, HardFork::Cancun); ingester.ingest_file(source(&["variable-push2", "main3.etk"]))?; assert_eq!(output, hex!("610107015801")); From 9c6297dccc584efface9a5321be8ae0c22806951 Mon Sep 17 00:00:00 2001 From: Gaston Zanitti Date: Fri, 10 Nov 2023 11:48:52 -0300 Subject: [PATCH 70/73] Updated to backend simplification (last version) --- etk-asm/tests/asm.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/etk-asm/tests/asm.rs b/etk-asm/tests/asm.rs index da0e1c57..0e549bc1 100644 --- a/etk-asm/tests/asm.rs +++ b/etk-asm/tests/asm.rs @@ -348,6 +348,7 @@ fn test_invalid_range_hardfork() { } ); } +#[test] fn test_dynamic_push2() -> Result<(), Error> { let mut output = Vec::new(); let mut ingester = Ingest::new(&mut output, HardFork::Cancun); From 445171f8312d430bb0d00a2c3cee9da08a86bba2 Mon Sep 17 00:00:00 2001 From: Gaston Zanitti Date: Fri, 10 Nov 2023 12:17:29 -0300 Subject: [PATCH 71/73] Minor fix test --- etk-asm/src/asm.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etk-asm/src/asm.rs b/etk-asm/src/asm.rs index 9d833d21..3007f0af 100644 --- a/etk-asm/src/asm.rs +++ b/etk-asm/src/asm.rs @@ -195,7 +195,7 @@ impl From<&AbstractOp> for RawOp { /// # /// # use hex_literal::hex; /// let mut asm = Assembler::new_with_hardfork(HardFork::Cancun); -/// let code = vec![AbstractOp::new(HardForkOp::Cancun(GetPc.into()))] +/// let code = vec![AbstractOp::new(HardForkOp::Cancun(GetPc.into()))]; /// let result = asm.assemble(&code)?; /// # assert_eq!(result, hex!("58")); /// # Result::<(), Error>::Ok(()) From 531c77ab71a168aa63830682e63024983e695ca4 Mon Sep 17 00:00:00 2001 From: Gaston Zanitti Date: Mon, 29 Jan 2024 17:17:10 -0300 Subject: [PATCH 72/73] clippy --- etk-asm/src/ingest.rs | 19 ++++++++----------- etk-asm/src/parse/macros.rs | 5 +---- etk-ops/build.rs | 24 ++++++++---------------- 3 files changed, 17 insertions(+), 31 deletions(-) diff --git a/etk-asm/src/ingest.rs b/etk-asm/src/ingest.rs index f5955b79..9acfc277 100644 --- a/etk-asm/src/ingest.rs +++ b/etk-asm/src/ingest.rs @@ -366,17 +366,14 @@ where } ); - match ophfd2 { - Some(hfd2) => { - ensure!( - self.hardfork.is_valid(&hfd2), - error::OutOfRangeHardfork { - hardfork: self.hardfork.clone(), - directive: hfd2, - } - ); - } - None => {} + if let Some(hfd2) = ophfd2 { + ensure!( + self.hardfork.is_valid(&hfd2), + error::OutOfRangeHardfork { + hardfork: self.hardfork.clone(), + directive: hfd2, + } + ); } } } diff --git a/etk-asm/src/parse/macros.rs b/etk-asm/src/parse/macros.rs index abcfbe40..ffc449b9 100644 --- a/etk-asm/src/parse/macros.rs +++ b/etk-asm/src/parse/macros.rs @@ -82,10 +82,7 @@ pub(crate) fn parse_builtin(pair: Pair) -> Result { } }; - directives.push(HardForkDirective { - operator, - hardfork: hardfork, - }); + directives.push(HardForkDirective { operator, hardfork }); if directives.len() > 2 { return ExceededRangeHardfork { parsed: directives.len(), diff --git a/etk-ops/build.rs b/etk-ops/build.rs index d658df7f..b21cde31 100644 --- a/etk-ops/build.rs +++ b/etk-ops/build.rs @@ -309,6 +309,7 @@ fn generate_fork(fork_name: &str) -> Result<(), Error> { }); } + let mut clone_bound = quote! {}; let mut partial_eq_bound = quote! {}; let mut eq_bound = quote! {}; let mut ord_bound = quote! {}; @@ -319,6 +320,10 @@ fn generate_fork(fork_name: &str) -> Result<(), Error> { for ii in 1..=32usize { let ident = format_ident!("P{}", ii); + clone_bound.extend(quote! { + T::#ident: Clone, + }); + partial_eq_bound.extend(quote! { T::#ident: std::cmp::PartialEq, }); @@ -342,6 +347,7 @@ fn generate_fork(fork_name: &str) -> Result<(), Error> { bounds.push(quote! { #ident }); } + let clone_bound = clone_bound.to_string(); let partial_eq_bound = partial_eq_bound.to_string(); let eq_bound = eq_bound.to_string(); let ord_bound = ord_bound.to_string(); @@ -352,6 +358,7 @@ fn generate_fork(fork_name: &str) -> Result<(), Error> { #[doc = concat!("All instructions in the ", #fork_name, " fork.")] #[derive(educe::Educe)] #[educe( + Clone(bound = #clone_bound), PartialEq(bound = #partial_eq_bound), Eq(bound = #eq_bound), Ord(bound = #ord_bound), @@ -376,22 +383,7 @@ fn generate_fork(fork_name: &str) -> Result<(), Error> { T: super::Immediates + ?Sized, { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "Op variant") - } - } - - // TODO: For some reason deriving Clone with educe didn't work. - impl Clone for Op - where - T: super::Immediates + ?Sized, - #(T::#bounds: Clone,)* - { - fn clone(&self) -> Self { - match self { - #( - Self::#names(n) => Self::#names(n.clone()), - )* - } + write!(f, "{}", self.mnemonic()) } } From e563f519d4adf503bbb8264a9de9ab581f3a467d Mon Sep 17 00:00:00 2001 From: Gaston Zanitti Date: Mon, 29 Jan 2024 17:29:34 -0300 Subject: [PATCH 73/73] directives.first() --- etk-asm/src/parse/macros.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/etk-asm/src/parse/macros.rs b/etk-asm/src/parse/macros.rs index ffc449b9..fdcaeb7e 100644 --- a/etk-asm/src/parse/macros.rs +++ b/etk-asm/src/parse/macros.rs @@ -116,7 +116,7 @@ fn hardfork_in_valid_range(directives: &[HardForkDirective]) -> Result<(), Parse match decresing_bound { Some(_) => { return OverlappingRangeHardfork { - directive0: directives.get(0), + directive0: directives.first(), directive1: directives.get(1), } .fail(); @@ -130,7 +130,7 @@ fn hardfork_in_valid_range(directives: &[HardForkDirective]) -> Result<(), Parse match incresing_bound { Some(_) => { return OverlappingRangeHardfork { - directive0: directives.get(0), + directive0: directives.first(), directive1: directives.get(1), } .fail(); @@ -142,7 +142,7 @@ fn hardfork_in_valid_range(directives: &[HardForkDirective]) -> Result<(), Parse } None => { return InvalidRangeHardfork { - directive0: directives.get(0), + directive0: directives.first(), directive1: directives.get(1), } .fail(); @@ -152,7 +152,7 @@ fn hardfork_in_valid_range(directives: &[HardForkDirective]) -> Result<(), Parse if incresing_bound.unwrap().hardfork > decresing_bound.unwrap().hardfork { return EmptyRangeHardfork { - directive0: directives.get(0), + directive0: directives.first(), directive1: directives.get(1), } .fail();