etk_asm/
asm.rs

1//! Single-scope assembler implementation and related types.
2//!
3//! See [`Assembler`] for more details on the low-level assembly process, or the
4//! [`mod@crate::ingest`] module for a higher-level interface.
5
6mod error {
7    use crate::ops::Expression;
8    use crate::ParseError;
9    use etk_ops::shanghai::Op;
10    use num_bigint::BigInt;
11    use snafu::{Backtrace, Snafu};
12
13    /// Errors that can occur while assembling instructions.
14    #[derive(Snafu, Debug)]
15    #[non_exhaustive]
16    #[snafu(context(suffix(false)), visibility(pub(super)))]
17    pub enum Error {
18        /// A label was declared multiple times.
19        #[snafu(display("label `{}` declared multiple times", label))]
20        #[non_exhaustive]
21        DuplicateLabel {
22            /// The name of the conflicting label.
23            label: String,
24
25            /// The location of the error.
26            backtrace: Backtrace,
27        },
28
29        /// A macro was declared multiple times.
30        #[snafu(display("macro `{}` declared multiple times", name))]
31        #[non_exhaustive]
32        DuplicateMacro {
33            /// The name of the conflicting macro.
34            name: String,
35
36            /// The location of the error.
37            backtrace: Backtrace,
38        },
39
40        /// A push instruction was too small for the result of the expression.
41        #[snafu(display(
42            "the expression `{}={}` was too large for the specifier {}",
43            expr,
44            value,
45            spec
46        ))]
47        #[non_exhaustive]
48        ExpressionTooLarge {
49            /// The oversized expression.
50            expr: Expression,
51
52            /// The evaluated value of the expression.
53            value: BigInt,
54
55            /// The specifier.
56            spec: Op<()>,
57
58            /// The location of the error.
59            backtrace: Backtrace,
60        },
61
62        /// An expression evaluated to a negative number.
63        #[snafu(display(
64            "the expression `{}={}` is negative and can't be represented as push operand",
65            expr,
66            value
67        ))]
68        ExpressionNegative {
69            /// The oversized expression.
70            expr: Expression,
71
72            /// The evaluated value of the expression.
73            value: BigInt,
74
75            /// The location of the error.
76            backtrace: Backtrace,
77        },
78
79        /// The value provided to an unsized push (`%push`) was too large.
80        #[snafu(display("value was too large for any push"))]
81        #[non_exhaustive]
82        UnsizedPushTooLarge {
83            /// The location of the error.
84            backtrace: Backtrace,
85        },
86
87        /// A label was used without being defined.
88        #[snafu(display("labels `{:?}` were never defined", labels))]
89        #[non_exhaustive]
90        UndeclaredLabels {
91            /// The labels that were used without being defined.
92            labels: Vec<String>,
93
94            /// The location of the error.
95            backtrace: Backtrace,
96        },
97
98        /// An instruction macro was used without being defined.
99        #[snafu(display("instruction macro `{}` was never defined", name))]
100        #[non_exhaustive]
101        UndeclaredInstructionMacro {
102            /// The macro that was used without being defined.
103            name: String,
104
105            /// The location of the error.
106            backtrace: Backtrace,
107        },
108
109        /// An expression macro was used without being defined.
110        #[snafu(display("expression macro `{}` was never defined", name))]
111        #[non_exhaustive]
112        UndeclaredExpressionMacro {
113            /// The macro that was used without being defined.
114            name: String,
115
116            /// The location of the error.
117            backtrace: Backtrace,
118        },
119
120        /// An import or include failed to parse.
121        #[snafu(display("include or import failed to parse: {}", source))]
122        #[snafu(context(false))]
123        #[non_exhaustive]
124        ParseInclude {
125            /// The next source of this error.
126            #[snafu(backtrace)]
127            source: ParseError,
128        },
129    }
130}
131
132pub use self::error::Error;
133use crate::ops::expression::{self, Terminal};
134use crate::ops::{self, AbstractOp, Assemble, Expression, Imm, MacroDefinition};
135use etk_ops::shanghai::Op;
136use rand::Rng;
137use snafu::OptionExt;
138use std::collections::{hash_map, HashMap, HashSet, VecDeque};
139
140/// An item to be assembled, which can be either an [`AbstractOp`] or a raw byte
141/// sequence.
142#[derive(Debug, Clone)]
143pub enum RawOp {
144    /// An instruction to be assembled.
145    Op(AbstractOp),
146
147    /// Raw bytes, for example from `%include_hex`, to be included verbatim in
148    /// the output.
149    Raw(Vec<u8>),
150}
151
152impl RawOp {
153    fn size(&self) -> Option<usize> {
154        match self {
155            Self::Op(op) => op.size(),
156            Self::Raw(raw) => Some(raw.len()),
157        }
158    }
159
160    fn expr(&self) -> Option<&Expression> {
161        match self {
162            Self::Op(op) => op.expr(),
163            Self::Raw(_) => None,
164        }
165    }
166}
167
168impl From<AbstractOp> for RawOp {
169    fn from(op: AbstractOp) -> Self {
170        Self::Op(op)
171    }
172}
173
174impl From<Vec<u8>> for RawOp {
175    fn from(vec: Vec<u8>) -> Self {
176        Self::Raw(vec)
177    }
178}
179
180/// Assembles a series of [`RawOp`] into raw bytes, tracking and resolving macros and labels,
181/// and handling dynamic pushes.
182///
183/// ## Example
184///
185/// ```rust
186/// use etk_asm::asm::Assembler;
187/// use etk_asm::ops::AbstractOp;
188/// use etk_ops::shanghai::{Op, GetPc};
189/// # use etk_asm::asm::Error;
190/// #
191/// # use hex_literal::hex;
192/// let mut asm = Assembler::new();
193/// asm.push_all(vec![
194///     AbstractOp::new(GetPc),
195/// ])?;
196/// let output = asm.take();
197/// asm.finish()?;
198/// # assert_eq!(output, hex!("58"));
199/// # Result::<(), Error>::Ok(())
200/// ```
201#[derive(Debug)]
202pub struct Assembler {
203    /// Assembled ops, ready to be taken.
204    ready: Vec<u8>,
205
206    /// Ops that cannot be encoded yet.
207    pending: VecDeque<RawOp>,
208
209    /// Sum of the size of all the ops in `pending`, or `None` if `pending` contains
210    /// an unsized op.
211    pending_len: Option<usize>,
212
213    /// Total number of `u8` that have been appended to `ready`.
214    concrete_len: usize,
215
216    /// Labels, in `pending`, associated with an `AbstractOp::Label`.
217    declared_labels: HashMap<String, Option<usize>>,
218
219    /// Macros, in `pending`, associated with an `AbstractOp::Macro`.
220    declared_macros: HashMap<String, MacroDefinition>,
221
222    /// Labels, in `pending`, that have been referred to (ex. with push) but
223    /// have not been declared with an `AbstractOp::Label`.
224    undeclared_labels: HashSet<String>,
225}
226
227impl Default for Assembler {
228    fn default() -> Self {
229        Self {
230            ready: Default::default(),
231            pending: Default::default(),
232            pending_len: Some(0),
233            concrete_len: 0,
234            declared_labels: Default::default(),
235            declared_macros: Default::default(),
236            undeclared_labels: Default::default(),
237        }
238    }
239}
240
241impl Assembler {
242    /// Create a new `Assembler`.
243    pub fn new() -> Self {
244        Self::default()
245    }
246
247    /// Indicate that the input sequence is complete. Returns any errors that
248    /// may remain.
249    pub fn finish(self) -> Result<(), Error> {
250        if let Some(undef) = self.pending.front() {
251            return match undef {
252                RawOp::Op(AbstractOp::Macro(invc)) => error::UndeclaredInstructionMacro {
253                    name: invc.name.clone(),
254                }
255                .fail(),
256                RawOp::Op(op) => {
257                    match op
258                        .clone()
259                        .concretize((&self.declared_labels, &self.declared_macros).into())
260                    {
261                        Ok(_) => unreachable!(),
262                        Err(ops::Error::ContextIncomplete {
263                            source: expression::Error::UnknownMacro { name, .. },
264                            ..
265                        }) => error::UndeclaredExpressionMacro { name }.fail(),
266                        Err(ops::Error::ContextIncomplete {
267                            source: expression::Error::UnknownLabel { .. },
268                            ..
269                        }) => {
270                            let labels = op.expr().unwrap().labels(&self.declared_macros).unwrap();
271                            let declared = self.declared_labels.into_keys().collect();
272                            let invoked: HashSet<_> = labels.into_iter().collect();
273                            let missing = invoked
274                                .difference(&declared)
275                                .cloned()
276                                .collect::<Vec<String>>();
277                            error::UndeclaredLabels { labels: missing }.fail()
278                        }
279                        _ => unreachable!(),
280                    }
281                }
282                // bug: if a variable is used when it isn't available, e.g. push1 $size
283                _ => unreachable!(),
284            };
285        }
286
287        if !self.ready.is_empty() {
288            panic!("not all assembled bytecode has been taken");
289        }
290
291        Ok(())
292    }
293
294    /// Collect any assembled instructions that are ready to be output.
295    pub fn take(&mut self) -> Vec<u8> {
296        std::mem::take(&mut self.ready)
297    }
298
299    /// Feed instructions into the `Assembler`.
300    ///
301    /// Returns the number of bytes that can be collected with [`Assembler::take`].
302    pub fn push_all<I, O>(&mut self, ops: I) -> Result<usize, Error>
303    where
304        I: IntoIterator<Item = O>,
305        O: Into<RawOp>,
306    {
307        for op in ops {
308            self.push(op)?;
309        }
310
311        Ok(self.ready.len())
312    }
313
314    /// Insert explicilty declared macros and labels, via `AbstractOp`, and implictly declared
315    /// macros and labels via usage in `Op`.
316    fn declare_content(&mut self, rop: &RawOp) -> Result<(), Error> {
317        match rop {
318            RawOp::Op(AbstractOp::Label(ref label)) => {
319                match self.declared_labels.entry(label.to_owned()) {
320                    hash_map::Entry::Occupied(_) => {
321                        return error::DuplicateLabel { label }.fail();
322                    }
323                    hash_map::Entry::Vacant(v) => {
324                        v.insert(None);
325                        self.undeclared_labels.remove(label);
326                    }
327                }
328            }
329            RawOp::Op(AbstractOp::MacroDefinition(ref defn)) => {
330                match self.declared_macros.entry(defn.name().to_owned()) {
331                    hash_map::Entry::Occupied(_) => {
332                        return error::DuplicateMacro { name: defn.name() }.fail()
333                    }
334                    hash_map::Entry::Vacant(v) => {
335                        v.insert(defn.to_owned());
336                    }
337                }
338            }
339            _ => (),
340        };
341
342        // Get all labels used by `rop`, check if they've been defined, and if not, note them as
343        // "undeclared".
344        if let Some(Ok(labels)) = rop.expr().map(|e| e.labels(&self.declared_macros)) {
345            for label in labels {
346                if !self.declared_labels.contains_key(&label) {
347                    self.undeclared_labels.insert(label.to_owned());
348                }
349            }
350        }
351
352        Ok(())
353    }
354
355    /// Feed a single instruction into the `Assembler`.
356    ///
357    /// Returns the number of bytes that can be collected with [`Assembler::take`]
358    pub fn push<O>(&mut self, rop: O) -> Result<usize, Error>
359    where
360        O: Into<RawOp>,
361    {
362        let rop = rop.into();
363
364        self.declare_content(&rop)?;
365
366        // Expand instruction macros immediately. We do this here because it's the same process
367        // regardless if we `push_read` or `push_pending` -- in fact, `expand_macro` pushes each op
368        // individually which calls the correct unchecked push.
369        if let RawOp::Op(AbstractOp::Macro(ref m)) = rop {
370            self.expand_macro(&m.name, &m.parameters)?;
371            return Ok(self.ready.len());
372        }
373
374        self.push_unchecked(rop)?;
375        Ok(self.ready.len())
376    }
377
378    fn push_unchecked(&mut self, rop: RawOp) -> Result<(), Error> {
379        if self.pending.is_empty() && self.pending_len.is_some() {
380            self.push_ready(rop)
381        } else {
382            self.push_pending(rop)
383        }
384    }
385
386    fn push_ready(&mut self, rop: RawOp) -> Result<(), Error> {
387        match rop {
388            RawOp::Op(AbstractOp::Label(label)) => {
389                let old = self
390                    .declared_labels
391                    .insert(label, Some(self.concrete_len))
392                    .expect("label should exist");
393                assert_eq!(old, None, "label should have been undefined");
394                Ok(())
395            }
396            RawOp::Op(AbstractOp::MacroDefinition(_)) => Ok(()),
397            RawOp::Op(AbstractOp::Macro(ref m)) => {
398                match self.declared_macros.get(&m.name) {
399                    // Do nothing if the instruction macro has been defined.
400                    Some(MacroDefinition::Instruction(_)) => (),
401                    _ => {
402                        assert_eq!(self.pending_len, Some(0));
403                        self.pending_len = None;
404                        self.pending.push_back(rop);
405                    }
406                }
407                Ok(())
408            }
409            RawOp::Op(ref op) => {
410                match op
411                    .clone()
412                    .concretize((&self.declared_labels, &self.declared_macros).into())
413                {
414                    Ok(cop) => {
415                        self.concrete_len += cop.size();
416                        cop.assemble(&mut self.ready);
417                    }
418                    Err(ops::Error::ExpressionTooLarge { value, spec, .. }) => {
419                        return error::ExpressionTooLarge {
420                            expr: op.expr().unwrap().clone(),
421                            value,
422                            spec,
423                        }
424                        .fail()
425                    }
426                    Err(ops::Error::ExpressionNegative { value, .. }) => {
427                        return error::ExpressionNegative {
428                            expr: op.expr().unwrap().clone(),
429                            value,
430                        }
431                        .fail()
432                    }
433                    Err(_) => {
434                        assert_eq!(self.pending_len, Some(0));
435                        self.pending_len = rop.size();
436                        self.pending.push_back(rop);
437                    }
438                }
439
440                Ok(())
441            }
442            RawOp::Raw(raw) => {
443                self.concrete_len += raw.len();
444                self.ready.extend(raw);
445                Ok(())
446            }
447        }
448    }
449
450    fn push_pending(&mut self, rop: RawOp) -> Result<(), Error> {
451        // Update total size of pending ops.
452        if let Some(ref mut pending_len) = self.pending_len {
453            match rop.size() {
454                Some(size) => *pending_len += size,
455                None => self.pending_len = None,
456            }
457        }
458
459        // Handle new label and macro definitions.
460        match (self.pending_len, rop) {
461            (Some(pending_len), RawOp::Op(AbstractOp::Label(lbl))) => {
462                // The label has a defined address.
463                let address = self.concrete_len + pending_len;
464                let item = self.declared_labels.get_mut(&*lbl).unwrap();
465                *item = Some(address);
466            }
467            (None, rop @ RawOp::Op(AbstractOp::Label(_))) => {
468                self.pending.push_back(rop);
469                if self.undeclared_labels.is_empty() {
470                    self.choose_sizes()?;
471                }
472            }
473            (_, RawOp::Op(AbstractOp::MacroDefinition(defn))) => {
474                if let Some(RawOp::Op(AbstractOp::Macro(invc))) = self.pending.front() {
475                    if defn.name() == &invc.name {
476                        let invc = invc.clone();
477                        self.pending.pop_front();
478                        self.expand_macro(&invc.name, &invc.parameters)?;
479                    }
480                }
481            }
482            (_, rop) => {
483                // Not a label.
484                self.pending.push_back(rop);
485            }
486        }
487
488        // Repeatedly check if the front of the pending list is ready.
489        while let Some(next) = self.pending.front() {
490            let op = match next {
491                RawOp::Op(AbstractOp::Push(Imm {
492                    tree: Expression::Terminal(Terminal::Label(_)),
493                    ..
494                })) => {
495                    if self.undeclared_labels.is_empty() {
496                        unreachable!()
497                    } else {
498                        // Still waiting on more labels.
499                        break;
500                    }
501                }
502                RawOp::Op(AbstractOp::Label(_)) => unreachable!(),
503                RawOp::Op(AbstractOp::Macro(_)) => {
504                    // Still waiting on more macros.
505                    break;
506                }
507                RawOp::Op(op) => op,
508                RawOp::Raw(_) => {
509                    self.pop_pending()?;
510                    continue;
511                }
512            };
513
514            match op
515                .clone()
516                .concretize((&self.declared_labels, &self.declared_macros).into())
517            {
518                Ok(cop) => {
519                    let front = self.pending.front_mut().unwrap();
520                    *front = RawOp::Op(cop.into());
521                }
522                Err(ops::Error::ExpressionTooLarge { value, spec, .. }) => {
523                    return error::ExpressionTooLarge {
524                        expr: op.expr().unwrap().clone(),
525                        value,
526                        spec,
527                    }
528                    .fail();
529                }
530                Err(_) => {
531                    // Still waiting for some definition.
532                    break;
533                }
534            }
535
536            self.pop_pending()?;
537        }
538
539        Ok(())
540    }
541
542    fn pop_pending(&mut self) -> Result<(), Error> {
543        let popped = self.pending.pop_front().unwrap();
544
545        let size;
546
547        match popped {
548            RawOp::Raw(raw) => {
549                size = raw.len();
550                self.ready.extend(raw);
551            }
552            RawOp::Op(aop) => {
553                let cop = aop
554                    .concretize((&self.declared_labels, &self.declared_macros).into())
555                    // Already able to concretize in `push_pending` loop.
556                    .unwrap();
557                size = cop.size();
558                cop.assemble(&mut self.ready);
559            }
560        }
561
562        self.concrete_len += size;
563
564        if self.pending.is_empty() {
565            self.pending_len = Some(0);
566        } else if let Some(ref mut pending_len) = self.pending_len {
567            *pending_len -= size;
568        }
569
570        Ok(())
571    }
572
573    fn choose_sizes(&mut self) -> Result<(), Error> {
574        let mut sizes: HashMap<Expression, Op<()>> = self
575            .pending
576            .iter()
577            .filter(|op| matches!(op, RawOp::Op(AbstractOp::Push(_))))
578            .map(|op| (op.expr().unwrap().clone(), Op::<()>::push_for(1).unwrap()))
579            .collect();
580
581        let mut subasm;
582
583        loop {
584            // Create a sub-assembler to try assembling with the sizes in
585            // `undefined_labels`.
586            subasm = Self::default();
587            subasm.concrete_len = self.concrete_len;
588            subasm.declared_labels = self.declared_labels.clone();
589
590            let result: Result<Vec<_>, Error> = self
591                .pending
592                .iter()
593                .map(|op| {
594                    let new = match op {
595                        RawOp::Op(AbstractOp::Push(Imm { tree, .. })) => {
596                            let new = sizes[tree].with(tree.clone()).unwrap();
597                            let aop = AbstractOp::new(new);
598                            RawOp::Op(aop)
599                        }
600                        op => op.clone(),
601                    };
602
603                    subasm.push_pending(new)
604                })
605                .collect();
606
607            match result {
608                Ok(_) => {
609                    assert!(subasm.pending.is_empty());
610                    break;
611                }
612                Err(Error::ExpressionTooLarge { expr, .. }) => {
613                    // If an expression is too large for an op, increase the width of that op.
614                    let item = sizes.get_mut(&expr).unwrap();
615                    let new_size = item.upsize().context(error::UnsizedPushTooLarge)?;
616                    *item = new_size;
617                }
618                Err(e) => return Err(e),
619            }
620        }
621
622        // Insert the results of the sub-assembler into self.
623        let raw = subasm.take();
624        self.pending_len = Some(raw.len());
625        self.pending.clear();
626        self.pending.push_back(RawOp::Raw(raw));
627        self.declared_labels = subasm.declared_labels;
628
629        Ok(())
630    }
631
632    fn expand_macro(
633        &mut self,
634        name: &str,
635        parameters: &[Expression],
636    ) -> Result<Option<usize>, Error> {
637        // Remap labels to macro scope.
638        match self.declared_macros.get(name).cloned() {
639            Some(MacroDefinition::Instruction(mut m)) => {
640                if m.parameters.len() != parameters.len() {
641                    panic!("invalid number of parameters for macro {}", name);
642                }
643
644                let parameters: HashMap<String, Expression> = m
645                    .parameters
646                    .into_iter()
647                    .zip(parameters.iter().cloned())
648                    .collect();
649
650                let mut labels = HashMap::<String, String>::new();
651                let mut rng = rand::thread_rng();
652
653                // First pass, find locally defined labels and rename them.
654                for op in m.contents.iter_mut() {
655                    match op {
656                        AbstractOp::Label(ref mut label) => {
657                            let mangled = format!("{}_{}_{}", m.name, label, rng.gen::<u64>());
658                            let old = labels.insert(label.to_owned(), mangled.clone());
659                            if old.is_some() {
660                                return error::DuplicateLabel {
661                                    label: label.to_string(),
662                                }
663                                .fail();
664                            }
665                            *label = mangled;
666                        }
667                        _ => continue,
668                    }
669                }
670
671                // Second pass, update local label invocations.
672                for op in m.contents.iter_mut() {
673                    if let Some(expr) = op.expr_mut() {
674                        for lbl in expr.labels(&self.declared_macros).unwrap() {
675                            if labels.contains_key(&lbl) {
676                                expr.replace_label(&lbl, &labels[&lbl]);
677                            }
678                        }
679                    }
680
681                    // Attempt to fill in parameters
682                    if let Some(expr) = op.expr_mut() {
683                        for (name, value) in parameters.iter() {
684                            expr.fill_variable(name, value)
685                        }
686                    }
687                }
688
689                Ok(Some(self.push_all(m.contents)?))
690            }
691            _ => {
692                assert_eq!(self.pending_len, Some(0));
693                self.pending_len = None;
694                self.pending.push_back(RawOp::Op(AbstractOp::Macro(
695                    ops::InstructionMacroInvocation {
696                        name: name.to_string(),
697                        parameters: parameters.to_vec(),
698                    },
699                )));
700                Ok(None)
701            }
702        }
703    }
704}
705
706#[cfg(test)]
707mod tests {
708    use super::*;
709    use crate::ops::{
710        Expression, ExpressionMacroDefinition, ExpressionMacroInvocation, Imm,
711        InstructionMacroDefinition, InstructionMacroInvocation, Terminal,
712    };
713    use assert_matches::assert_matches;
714    use etk_ops::shanghai::*;
715    use hex_literal::hex;
716    use num_bigint::{BigInt, Sign};
717
718    #[test]
719    fn assemble_variable_push_const_while_pending() -> Result<(), Error> {
720        let mut asm = Assembler::new();
721        let sz = asm.push_all(vec![
722            AbstractOp::Op(Push1(Imm::with_label("label1")).into()),
723            AbstractOp::Push(Terminal::Number(0xaabb.into()).into()),
724            AbstractOp::Label("label1".into()),
725        ])?;
726        assert_eq!(5, sz);
727        assert_eq!(asm.take(), hex!("600561aabb"));
728        Ok(())
729    }
730
731    #[test]
732    fn assemble_variable_pushes_abab() -> Result<(), Error> {
733        let mut asm = Assembler::new();
734        let sz = asm.push_all(vec![
735            AbstractOp::new(JumpDest),
736            AbstractOp::Push(Imm::with_label("label1")),
737            AbstractOp::Push(Imm::with_label("label2")),
738            AbstractOp::Label("label1".into()),
739            AbstractOp::new(GetPc),
740            AbstractOp::Label("label2".into()),
741            AbstractOp::new(GetPc),
742        ])?;
743        assert_eq!(7, sz);
744        assert_eq!(asm.take(), hex!("5b600560065858"));
745        Ok(())
746    }
747
748    #[test]
749    fn assemble_variable_pushes_abba() -> Result<(), Error> {
750        let mut asm = Assembler::new();
751        let sz = asm.push_all(vec![
752            AbstractOp::new(JumpDest),
753            AbstractOp::Push(Imm::with_label("label1")),
754            AbstractOp::Push(Imm::with_label("label2")),
755            AbstractOp::Label("label2".into()),
756            AbstractOp::new(GetPc),
757            AbstractOp::Label("label1".into()),
758            AbstractOp::new(GetPc),
759        ])?;
760        assert_eq!(7, sz);
761        assert_eq!(asm.take(), hex!("5b600660055858"));
762        Ok(())
763    }
764
765    #[test]
766    fn assemble_variable_push1_multiple() -> Result<(), Error> {
767        let mut asm = Assembler::new();
768        let sz = asm.push_all(vec![
769            AbstractOp::new(JumpDest),
770            AbstractOp::Push(Imm::with_label("auto")),
771            AbstractOp::Push(Imm::with_label("auto")),
772            AbstractOp::Label("auto".into()),
773        ])?;
774        assert_eq!(5, sz);
775        assert_eq!(asm.take(), hex!("5b60056005"));
776        Ok(())
777    }
778
779    #[test]
780    fn assemble_variable_push_const() -> Result<(), Error> {
781        let mut asm = Assembler::new();
782        let sz = asm.push_all(vec![AbstractOp::Push(
783            Terminal::Number((0x00aaaaaaaaaaaaaaaaaaaaaaaa as u128).into()).into(),
784        )])?;
785        assert_eq!(13, sz);
786        assert_eq!(asm.take(), hex!("6baaaaaaaaaaaaaaaaaaaaaaaa"));
787        Ok(())
788    }
789
790    #[test]
791    fn assemble_variable_push_too_large() {
792        let v = BigInt::from_bytes_be(Sign::Plus, &[1u8; 33]);
793
794        let mut asm = Assembler::new();
795        let err = asm
796            .push_all(vec![AbstractOp::Push(Terminal::Number(v).into())])
797            .unwrap_err();
798
799        assert_matches!(err, Error::ExpressionTooLarge { .. });
800    }
801
802    #[test]
803    fn assemble_variable_push_negative() {
804        let mut asm = Assembler::new();
805        let err = asm
806            .push_all(vec![AbstractOp::Push(Terminal::Number((-1).into()).into())])
807            .unwrap_err();
808
809        assert_matches!(err, Error::ExpressionNegative { .. });
810    }
811
812    #[test]
813    fn assemble_variable_push_const0() -> Result<(), Error> {
814        let mut asm = Assembler::new();
815        let sz = asm.push_all(vec![AbstractOp::Push(
816            Terminal::Number((0x00 as u128).into()).into(),
817        )])?;
818        assert_eq!(2, sz);
819        assert_eq!(asm.take(), hex!("6000"));
820        Ok(())
821    }
822
823    #[test]
824    fn assemble_variable_push1_known() -> Result<(), Error> {
825        let mut asm = Assembler::new();
826        let sz = asm.push_all(vec![
827            AbstractOp::new(JumpDest),
828            AbstractOp::Label("auto".into()),
829            AbstractOp::Push(Imm::with_label("auto")),
830        ])?;
831        assert_eq!(3, sz);
832        assert_eq!(asm.take(), hex!("5b6001"));
833        Ok(())
834    }
835
836    #[test]
837    fn assemble_variable_push1() -> Result<(), Error> {
838        let mut asm = Assembler::new();
839        let sz = asm.push_all(vec![
840            AbstractOp::Push(Imm::with_label("auto")),
841            AbstractOp::Label("auto".into()),
842            AbstractOp::new(JumpDest),
843        ])?;
844        assert_eq!(3, sz);
845        assert_eq!(asm.take(), hex!("60025b"));
846        Ok(())
847    }
848
849    #[test]
850    fn assemble_variable_push1_reuse() -> Result<(), Error> {
851        let mut asm = Assembler::new();
852        let sz = asm.push_all(vec![
853            AbstractOp::Push(Imm::with_label("auto")),
854            AbstractOp::Label("auto".into()),
855            AbstractOp::new(JumpDest),
856            AbstractOp::new(Push1(Imm::with_label("auto"))),
857        ])?;
858        assert_eq!(5, sz);
859        assert_eq!(asm.take(), hex!("60025b6002"));
860        Ok(())
861    }
862
863    #[test]
864    fn assemble_variable_push2() -> Result<(), Error> {
865        let mut asm = Assembler::new();
866        asm.push(AbstractOp::Push(Imm::with_label("auto")))?;
867        for _ in 0..255 {
868            asm.push(AbstractOp::new(GetPc))?;
869        }
870
871        asm.push_all(vec![
872            AbstractOp::Label("auto".into()),
873            AbstractOp::new(JumpDest),
874        ])?;
875
876        let mut expected = vec![0x61, 0x01, 0x02];
877        expected.extend_from_slice(&[0x58; 255]);
878        expected.push(0x5b);
879        assert_eq!(asm.take(), expected);
880
881        asm.finish()?;
882
883        Ok(())
884    }
885
886    #[test]
887    fn assemble_undeclared_label() -> Result<(), Error> {
888        let mut asm = Assembler::new();
889        asm.push_all(vec![AbstractOp::new(Push1(Imm::with_label("hi")))])?;
890        let err = asm.finish().unwrap_err();
891        assert_matches!(err, Error::UndeclaredLabels { labels, .. } if labels == vec!["hi"]);
892        Ok(())
893    }
894
895    #[test]
896    fn assemble_jumpdest_no_label() -> Result<(), Error> {
897        let mut asm = Assembler::new();
898        let sz = asm.push_all(vec![AbstractOp::new(JumpDest)])?;
899        assert_eq!(1, sz);
900        assert!(asm.declared_labels.is_empty());
901        assert_eq!(asm.take(), hex!("5b"));
902        Ok(())
903    }
904
905    #[test]
906    fn assemble_jumpdest_with_label() -> Result<(), Error> {
907        let mut asm = Assembler::new();
908        let ops = vec![AbstractOp::Label("lbl".into()), AbstractOp::new(JumpDest)];
909
910        let sz = asm.push_all(ops)?;
911        assert_eq!(1, sz);
912        assert_eq!(asm.declared_labels.len(), 1);
913        assert_eq!(asm.declared_labels.get("lbl"), Some(&Some(0)));
914        assert_eq!(asm.take(), hex!("5b"));
915        Ok(())
916    }
917
918    #[test]
919    fn assemble_jumpdest_jump_with_label() -> Result<(), Error> {
920        let ops = vec![
921            AbstractOp::Label("lbl".into()),
922            AbstractOp::new(JumpDest),
923            AbstractOp::new(Push1(Imm::with_label("lbl"))),
924        ];
925
926        let mut asm = Assembler::new();
927        let sz = asm.push_all(ops)?;
928        assert_eq!(sz, 3);
929        assert_eq!(asm.take(), hex!("5b6000"));
930
931        Ok(())
932    }
933
934    #[test]
935    fn assemble_labeled_pc() -> Result<(), Error> {
936        let ops = vec![
937            AbstractOp::new(Push1(Imm::with_label("lbl"))),
938            AbstractOp::Label("lbl".into()),
939            AbstractOp::new(GetPc),
940        ];
941
942        let mut asm = Assembler::new();
943        let sz = asm.push_all(ops)?;
944        assert_eq!(sz, 3);
945        assert_eq!(asm.take(), hex!("600258"));
946
947        Ok(())
948    }
949
950    #[test]
951    fn assemble_jump_jumpdest_with_label() -> Result<(), Error> {
952        let ops = vec![
953            AbstractOp::new(Push1(Imm::with_label("lbl"))),
954            AbstractOp::Label("lbl".into()),
955            AbstractOp::new(JumpDest),
956        ];
957
958        let mut asm = Assembler::new();
959        let sz = asm.push_all(ops)?;
960        assert_eq!(sz, 3);
961        assert_eq!(asm.take(), hex!("60025b"));
962
963        Ok(())
964    }
965
966    #[test]
967    fn assemble_label_too_large() {
968        let mut ops: Vec<_> = vec![AbstractOp::new(GetPc); 255];
969        ops.push(AbstractOp::Label("b".into()));
970        ops.push(AbstractOp::new(JumpDest));
971        ops.push(AbstractOp::Label("a".into()));
972        ops.push(AbstractOp::new(JumpDest));
973        ops.push(AbstractOp::new(Push1(Imm::with_label("a"))));
974        let mut asm = Assembler::new();
975        let err = asm.push_all(ops).unwrap_err();
976        assert_matches!(err, Error::ExpressionTooLarge { expr: Expression::Terminal(Terminal::Label(label)), .. } if label == "a");
977    }
978
979    #[test]
980    fn assemble_label_just_right() -> Result<(), Error> {
981        let mut ops: Vec<_> = vec![AbstractOp::new(GetPc); 255];
982        ops.push(AbstractOp::Label("b".into()));
983        ops.push(AbstractOp::new(JumpDest));
984        ops.push(AbstractOp::Label("a".into()));
985        ops.push(AbstractOp::new(JumpDest));
986        ops.push(AbstractOp::new(Push1(Imm::with_label("b"))));
987        let mut asm = Assembler::new();
988        let sz = asm.push_all(ops)?;
989        assert_eq!(sz, 259);
990
991        let assembled = asm.take();
992        asm.finish()?;
993
994        let mut expected = vec![0x58; 255];
995        expected.push(0x5b);
996        expected.push(0x5b);
997        expected.push(0x60);
998        expected.push(0xff);
999
1000        assert_eq!(assembled, expected);
1001
1002        Ok(())
1003    }
1004
1005    #[test]
1006    fn assemble_instruction_macro_label_underscore() -> Result<(), Error> {
1007        let ops = vec![
1008            InstructionMacroDefinition {
1009                name: "my_macro".into(),
1010                parameters: vec![],
1011                contents: vec![AbstractOp::Label("a".into())],
1012            }
1013            .into(),
1014            InstructionMacroDefinition {
1015                name: "my".into(),
1016                parameters: vec![],
1017                contents: vec![AbstractOp::Label("macro_a".into())],
1018            }
1019            .into(),
1020            AbstractOp::Macro(InstructionMacroInvocation {
1021                name: "my_macro".into(),
1022                parameters: vec![],
1023            }),
1024            AbstractOp::Macro(InstructionMacroInvocation {
1025                name: "my".into(),
1026                parameters: vec![],
1027            }),
1028        ];
1029
1030        let mut asm = Assembler::new();
1031        let sz = asm.push_all(ops)?;
1032        assert_eq!(sz, 0);
1033        let out = asm.take();
1034        assert_eq!(out, []);
1035
1036        Ok(())
1037    }
1038
1039    #[test]
1040    fn assemble_instruction_macro_twice() -> Result<(), Error> {
1041        let ops = vec![
1042            InstructionMacroDefinition {
1043                name: "my_macro".into(),
1044                parameters: vec![],
1045                contents: vec![
1046                    AbstractOp::Label("a".into()),
1047                    AbstractOp::new(JumpDest),
1048                    AbstractOp::new(Push1(Imm::with_label("a"))),
1049                    AbstractOp::new(Push1(Imm::with_label("b"))),
1050                ],
1051            }
1052            .into(),
1053            AbstractOp::Label("b".into()),
1054            AbstractOp::new(JumpDest),
1055            AbstractOp::new(Push1(Imm::with_label("b"))),
1056            AbstractOp::Macro(InstructionMacroInvocation {
1057                name: "my_macro".into(),
1058                parameters: vec![],
1059            }),
1060            AbstractOp::Macro(InstructionMacroInvocation {
1061                name: "my_macro".into(),
1062                parameters: vec![],
1063            }),
1064        ];
1065
1066        let mut asm = Assembler::new();
1067        let sz = asm.push_all(ops)?;
1068        assert_eq!(sz, 13);
1069        let out = asm.take();
1070        assert_eq!(out, hex!("5b60005b600360005b60086000"));
1071
1072        Ok(())
1073    }
1074
1075    #[test]
1076    fn assemble_instruction_macro() -> Result<(), Error> {
1077        let ops = vec![
1078            InstructionMacroDefinition {
1079                name: "my_macro".into(),
1080                parameters: vec![],
1081                contents: vec![
1082                    AbstractOp::Label("a".into()),
1083                    AbstractOp::new(JumpDest),
1084                    AbstractOp::new(Push1(Imm::with_label("a"))),
1085                    AbstractOp::new(Push1(Imm::with_label("b"))),
1086                ],
1087            }
1088            .into(),
1089            AbstractOp::Label("b".into()),
1090            AbstractOp::new(JumpDest),
1091            AbstractOp::new(Push1(Imm::with_label("b"))),
1092            AbstractOp::Macro(InstructionMacroInvocation {
1093                name: "my_macro".into(),
1094                parameters: vec![],
1095            }),
1096        ];
1097
1098        let mut asm = Assembler::new();
1099        let sz = asm.push_all(ops)?;
1100        assert_eq!(sz, 8);
1101        let out = asm.take();
1102        assert_eq!(out, hex!("5b60005b60036000"));
1103
1104        Ok(())
1105    }
1106
1107    #[test]
1108    fn assemble_instruction_macro_delayed_definition() -> Result<(), Error> {
1109        let ops = vec![
1110            AbstractOp::Label("b".into()),
1111            AbstractOp::new(JumpDest),
1112            AbstractOp::new(Push1(Imm::with_label("b"))),
1113            AbstractOp::Macro(InstructionMacroInvocation {
1114                name: "my_macro".into(),
1115                parameters: vec![],
1116            }),
1117            InstructionMacroDefinition {
1118                name: "my_macro".into(),
1119                parameters: vec![],
1120                contents: vec![
1121                    AbstractOp::Label("a".into()),
1122                    AbstractOp::new(JumpDest),
1123                    AbstractOp::new(Push1(Imm::with_label("a"))),
1124                    AbstractOp::new(Push1(Imm::with_label("b"))),
1125                ],
1126            }
1127            .into(),
1128        ];
1129
1130        let mut asm = Assembler::new();
1131        let sz = asm.push_all(ops)?;
1132        assert_eq!(sz, 8);
1133        let out = asm.take();
1134        assert_eq!(out, hex!("5b60005b60036000"));
1135
1136        Ok(())
1137    }
1138
1139    #[test]
1140    fn assemble_instruction_macro_with_variable_push() -> Result<(), Error> {
1141        let ops = vec![
1142            AbstractOp::Macro(InstructionMacroInvocation {
1143                name: "my_macro".into(),
1144                parameters: vec![],
1145            }),
1146            InstructionMacroDefinition {
1147                name: "my_macro".into(),
1148                parameters: vec![],
1149                contents: vec![
1150                    AbstractOp::new(JumpDest),
1151                    AbstractOp::Push(Imm::with_label("label1")),
1152                    AbstractOp::Push(Imm::with_label("label2")),
1153                    AbstractOp::Label("label1".into()),
1154                    AbstractOp::new(GetPc),
1155                    AbstractOp::Label("label2".into()),
1156                    AbstractOp::new(GetPc),
1157                ],
1158            }
1159            .into(),
1160        ];
1161
1162        let mut asm = Assembler::new();
1163        let sz = asm.push_all(ops)?;
1164        assert_eq!(7, sz);
1165        assert_eq!(asm.take(), hex!("5b600560065858"));
1166
1167        Ok(())
1168    }
1169
1170    #[test]
1171    fn assemble_undeclared_instruction_macro() -> Result<(), Error> {
1172        let ops = vec![AbstractOp::Macro(
1173            InstructionMacroInvocation::with_zero_parameters("my_macro".into()),
1174        )];
1175        let mut asm = Assembler::new();
1176        asm.push_all(ops)?;
1177        let err = asm.finish().unwrap_err();
1178        assert_matches!(err, Error::UndeclaredInstructionMacro { name, .. } if name == "my_macro");
1179
1180        Ok(())
1181    }
1182
1183    #[test]
1184    fn assemble_duplicate_instruction_macro() -> Result<(), Error> {
1185        let ops: Vec<AbstractOp> = vec![
1186            InstructionMacroDefinition {
1187                name: "my_macro".into(),
1188                parameters: vec![],
1189                contents: vec![AbstractOp::new(Caller)],
1190            }
1191            .into(),
1192            InstructionMacroDefinition {
1193                name: "my_macro".into(),
1194                parameters: vec![],
1195                contents: vec![AbstractOp::new(Caller)],
1196            }
1197            .into(),
1198        ];
1199        let mut asm = Assembler::new();
1200        let err = asm.push_all(ops).unwrap_err();
1201        assert_matches!(err, Error::DuplicateMacro { name, .. } if name == "my_macro");
1202
1203        Ok(())
1204    }
1205
1206    #[test]
1207    fn assemble_duplicate_labels_in_instruction_macro() -> Result<(), Error> {
1208        let ops = vec![
1209            InstructionMacroDefinition {
1210                name: "my_macro".into(),
1211                parameters: vec![],
1212                contents: vec![AbstractOp::Label("a".into()), AbstractOp::Label("a".into())],
1213            }
1214            .into(),
1215            AbstractOp::Macro(InstructionMacroInvocation::with_zero_parameters(
1216                "my_macro".into(),
1217            )),
1218        ];
1219        let mut asm = Assembler::new();
1220        let err = asm.push_all(ops).unwrap_err();
1221        assert_matches!(err, Error::DuplicateLabel { label, .. } if label == "a");
1222
1223        Ok(())
1224    }
1225
1226    // TODO: do we allow label shadowing in macros?
1227    #[test]
1228    fn assemble_conflicting_labels_in_instruction_macro() -> Result<(), Error> {
1229        let ops = vec![
1230            AbstractOp::Label("a".into()),
1231            AbstractOp::new(Caller),
1232            InstructionMacroDefinition {
1233                name: "my_macro()".into(),
1234                parameters: vec![],
1235                contents: vec![
1236                    AbstractOp::Label("a".into()),
1237                    AbstractOp::new(Push1(Imm::with_label("a"))),
1238                ],
1239            }
1240            .into(),
1241            AbstractOp::Macro(InstructionMacroInvocation::with_zero_parameters(
1242                "my_macro()".into(),
1243            )),
1244            AbstractOp::new(Push1(Imm::with_label("a"))),
1245        ];
1246        let mut asm = Assembler::new();
1247        let sz = asm.push_all(ops)?;
1248        assert_eq!(sz, 5);
1249
1250        let out = asm.take();
1251        asm.finish()?;
1252
1253        assert_eq!(out, hex!("3360016000"));
1254
1255        Ok(())
1256    }
1257
1258    #[test]
1259    fn assemble_instruction_macro_with_parameters() -> Result<(), Error> {
1260        let ops = vec![
1261            InstructionMacroDefinition {
1262                name: "my_macro".into(),
1263                parameters: vec!["foo".into(), "bar".into()],
1264                contents: vec![
1265                    AbstractOp::new(Push1(Imm::with_variable("foo"))),
1266                    AbstractOp::new(Push1(Imm::with_variable("bar"))),
1267                ],
1268            }
1269            .into(),
1270            AbstractOp::Label("b".into()),
1271            AbstractOp::new(JumpDest),
1272            AbstractOp::new(Push1(Imm::with_label("b"))),
1273            AbstractOp::Macro(InstructionMacroInvocation {
1274                name: "my_macro".into(),
1275                parameters: vec![
1276                    BigInt::from_bytes_be(Sign::Plus, &vec![0x42]).into(),
1277                    Terminal::Label("b".to_string()).into(),
1278                ],
1279            }),
1280        ];
1281
1282        let mut asm = Assembler::new();
1283        let sz = asm.push_all(ops)?;
1284        assert_eq!(sz, 7);
1285        let out = asm.take();
1286        assert_eq!(out, hex!("5b600060426000"));
1287
1288        Ok(())
1289    }
1290
1291    #[test]
1292    fn assemble_expression_push() -> Result<(), Error> {
1293        let ops = vec![AbstractOp::new(Push1(Imm::with_expression(
1294            Expression::Plus(1.into(), 1.into()),
1295        )))];
1296
1297        let mut asm = Assembler::new();
1298        let sz = asm.push_all(ops)?;
1299        assert_eq!(sz, 2);
1300        let out = asm.take();
1301        assert_eq!(out, hex!("6002"));
1302
1303        Ok(())
1304    }
1305
1306    #[test]
1307    fn assemble_expression_negative() -> Result<(), Error> {
1308        let ops = vec![AbstractOp::new(Push1(Imm::with_expression(
1309            BigInt::from(-1).into(),
1310        )))];
1311        let mut asm = Assembler::new();
1312        let err = asm.push_all(ops).unwrap_err();
1313        assert_matches!(err, Error::ExpressionNegative { value, .. } if value == BigInt::from(-1));
1314
1315        Ok(())
1316    }
1317
1318    #[test]
1319    fn assemble_expression_undeclared_label() -> Result<(), Error> {
1320        let mut asm = Assembler::new();
1321        asm.push_all(vec![AbstractOp::new(Push1(Imm::with_expression(
1322            Terminal::Label(String::from("hi")).into(),
1323        )))])?;
1324        let err = asm.finish().unwrap_err();
1325        assert_matches!(err, Error::UndeclaredLabels { labels, .. } if labels == vec!["hi"]);
1326        Ok(())
1327    }
1328
1329    #[test]
1330    fn assemble_variable_push_expression_with_undeclared_labels() -> Result<(), Error> {
1331        let mut asm = Assembler::new();
1332        asm.push_all(vec![
1333            AbstractOp::new(JumpDest),
1334            AbstractOp::Push(Imm::with_expression(Expression::Plus(
1335                Terminal::Label("foo".into()).into(),
1336                Terminal::Label("bar".into()).into(),
1337            ))),
1338            AbstractOp::new(Gas),
1339        ])?;
1340        let err = asm.finish().unwrap_err();
1341        assert_matches!(err, Error::UndeclaredLabels { labels, .. } if (labels.contains(&"foo".to_string())) && labels.contains(&"bar".to_string()));
1342        Ok(())
1343    }
1344
1345    #[test]
1346    fn assemble_variable_push1_expression() -> Result<(), Error> {
1347        let mut asm = Assembler::new();
1348        let sz = asm.push_all(vec![
1349            AbstractOp::new(JumpDest),
1350            AbstractOp::Label("auto".into()),
1351            AbstractOp::Push(Imm::with_expression(Expression::Plus(
1352                1.into(),
1353                Terminal::Label(String::from("auto")).into(),
1354            ))),
1355        ])?;
1356        assert_eq!(3, sz);
1357        assert_eq!(asm.take(), hex!("5b6002"));
1358        Ok(())
1359    }
1360
1361    #[test]
1362    fn assemble_expression_with_labels() -> Result<(), Error> {
1363        let mut asm = Assembler::new();
1364        let sz = asm.push_all(vec![
1365            AbstractOp::new(JumpDest),
1366            AbstractOp::Push(Imm::with_expression(Expression::Plus(
1367                Terminal::Label(String::from("foo")).into(),
1368                Terminal::Label(String::from("bar")).into(),
1369            ))),
1370            AbstractOp::new(Gas),
1371            AbstractOp::Label("foo".into()),
1372            AbstractOp::Label("bar".into()),
1373        ])?;
1374        assert_eq!(4, sz);
1375        assert_eq!(asm.take(), hex!("5b60085a"));
1376        Ok(())
1377    }
1378
1379    #[test]
1380    fn assemble_expression_macro_push() -> Result<(), Error> {
1381        let ops = vec![
1382            ExpressionMacroDefinition {
1383                name: "foo".into(),
1384                parameters: vec![],
1385                content: Imm::with_expression(Expression::Plus(1.into(), 1.into())),
1386            }
1387            .into(),
1388            AbstractOp::new(Push1(Imm::with_macro(ExpressionMacroInvocation {
1389                name: "foo".into(),
1390                parameters: vec![],
1391            }))),
1392        ];
1393
1394        let mut asm = Assembler::new();
1395        let sz = asm.push_all(ops)?;
1396        assert_eq!(sz, 2);
1397        let out = asm.take();
1398        assert_eq!(out, hex!("6002"));
1399
1400        Ok(())
1401    }
1402}