1mod 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 #[derive(Snafu, Debug)]
15 #[non_exhaustive]
16 #[snafu(context(suffix(false)), visibility(pub(super)))]
17 pub enum Error {
18 #[snafu(display("label `{}` declared multiple times", label))]
20 #[non_exhaustive]
21 DuplicateLabel {
22 label: String,
24
25 backtrace: Backtrace,
27 },
28
29 #[snafu(display("macro `{}` declared multiple times", name))]
31 #[non_exhaustive]
32 DuplicateMacro {
33 name: String,
35
36 backtrace: Backtrace,
38 },
39
40 #[snafu(display(
42 "the expression `{}={}` was too large for the specifier {}",
43 expr,
44 value,
45 spec
46 ))]
47 #[non_exhaustive]
48 ExpressionTooLarge {
49 expr: Expression,
51
52 value: BigInt,
54
55 spec: Op<()>,
57
58 backtrace: Backtrace,
60 },
61
62 #[snafu(display(
64 "the expression `{}={}` is negative and can't be represented as push operand",
65 expr,
66 value
67 ))]
68 ExpressionNegative {
69 expr: Expression,
71
72 value: BigInt,
74
75 backtrace: Backtrace,
77 },
78
79 #[snafu(display("value was too large for any push"))]
81 #[non_exhaustive]
82 UnsizedPushTooLarge {
83 backtrace: Backtrace,
85 },
86
87 #[snafu(display("labels `{:?}` were never defined", labels))]
89 #[non_exhaustive]
90 UndeclaredLabels {
91 labels: Vec<String>,
93
94 backtrace: Backtrace,
96 },
97
98 #[snafu(display("instruction macro `{}` was never defined", name))]
100 #[non_exhaustive]
101 UndeclaredInstructionMacro {
102 name: String,
104
105 backtrace: Backtrace,
107 },
108
109 #[snafu(display("expression macro `{}` was never defined", name))]
111 #[non_exhaustive]
112 UndeclaredExpressionMacro {
113 name: String,
115
116 backtrace: Backtrace,
118 },
119
120 #[snafu(display("include or import failed to parse: {}", source))]
122 #[snafu(context(false))]
123 #[non_exhaustive]
124 ParseInclude {
125 #[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#[derive(Debug, Clone)]
143pub enum RawOp {
144 Op(AbstractOp),
146
147 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#[derive(Debug)]
202pub struct Assembler {
203 ready: Vec<u8>,
205
206 pending: VecDeque<RawOp>,
208
209 pending_len: Option<usize>,
212
213 concrete_len: usize,
215
216 declared_labels: HashMap<String, Option<usize>>,
218
219 declared_macros: HashMap<String, MacroDefinition>,
221
222 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 pub fn new() -> Self {
244 Self::default()
245 }
246
247 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 _ => 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 pub fn take(&mut self) -> Vec<u8> {
296 std::mem::take(&mut self.ready)
297 }
298
299 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 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 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 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 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 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 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 match (self.pending_len, rop) {
461 (Some(pending_len), RawOp::Op(AbstractOp::Label(lbl))) => {
462 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 self.pending.push_back(rop);
485 }
486 }
487
488 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 break;
500 }
501 }
502 RawOp::Op(AbstractOp::Label(_)) => unreachable!(),
503 RawOp::Op(AbstractOp::Macro(_)) => {
504 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 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 .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 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 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 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 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 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 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 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 #[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}