1extern crate proc_macro;
2use heck::ToUpperCamelCase;
3use proc_macro::TokenStream;
4use quote::quote;
5use syn::{
6 Expr, Ident, LitInt, Token, bracketed, parenthesized, parse::Parse, parse_macro_input,
7 spanned::Spanned,
8};
9
10#[derive(Clone)]
11enum StackItem {
12 Name(Ident),
13 NameCounted(Ident, Expr),
14 Unused(Expr),
16}
17
18#[derive(Clone)]
19struct StackEffect {
20 pops: Vec<StackItem>,
21 pushes: Vec<StackItem>,
22}
23
24#[derive(Clone)]
25struct Opcode {
26 name: Ident,
27 number: LitInt,
28 stack_effect: Option<StackEffect>,
29}
30
31struct Exception {
32 stack_effect: StackEffect,
33}
34
35impl Parse for Opcode {
36 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
37 let name: Ident = input.parse()?;
40 input.parse::<Token![=]>()?;
41 let number: LitInt = input.parse()?;
42
43 let inner_stack_effect;
44
45 parenthesized!(inner_stack_effect in input);
46
47 let mut stack_effect = StackEffect {
48 pops: vec![],
49 pushes: vec![],
50 };
51
52 if inner_stack_effect.parse::<Token![/]>().is_ok() {
53 return Ok(Opcode {
55 name,
56 number,
57 stack_effect: None,
58 });
59 }
60
61 while inner_stack_effect.peek(Ident) {
63 let name: Ident = inner_stack_effect.parse()?;
64
65 stack_effect.pops.push(
66 if name == "unused" {
68 if inner_stack_effect.peek(syn::token::Bracket) {
69 let inner_bracket;
70 bracketed!(inner_bracket in inner_stack_effect);
71 let size: Expr = inner_bracket.parse()?;
72 StackItem::Unused(size)
73 } else {
74 StackItem::Unused(Expr::Lit(syn::ExprLit {
75 attrs: vec![],
76 lit: syn::Lit::Int(LitInt::new(
77 "1",
78 proc_macro::Span::call_site().into(),
79 )),
80 }))
81 }
82 } else {
83 if inner_stack_effect.peek(syn::token::Bracket) {
84 let inner_bracket;
85 bracketed!(inner_bracket in inner_stack_effect);
86 let size: Expr = inner_bracket.parse()?;
87 StackItem::NameCounted(name, size)
88 } else {
89 StackItem::Name(name)
90 }
91 },
92 );
93
94 if inner_stack_effect.parse::<Token![,]>().is_err() {
95 break;
96 }
97 }
98
99 inner_stack_effect.parse::<Token![-]>()?;
100 inner_stack_effect.parse::<Token![-]>()?;
101
102 while inner_stack_effect.peek(Ident) {
103 let name: Ident = inner_stack_effect.parse()?;
104
105 stack_effect.pushes.push(
106 if name == "unused" {
108 if inner_stack_effect.peek(syn::token::Bracket) {
109 let inner_bracket;
110 bracketed!(inner_bracket in inner_stack_effect);
111 let size: Expr = inner_bracket.parse()?;
112 StackItem::Unused(size)
113 } else {
114 StackItem::Unused(Expr::Lit(syn::ExprLit {
115 attrs: vec![],
116 lit: syn::Lit::Int(LitInt::new(
117 "1",
118 proc_macro::Span::call_site().into(),
119 )),
120 }))
121 }
122 } else {
123 if inner_stack_effect.peek(syn::token::Bracket) {
124 let inner_bracket;
125 bracketed!(inner_bracket in inner_stack_effect);
126 let size: Expr = inner_bracket.parse()?;
127 StackItem::NameCounted(name, size)
128 } else {
129 StackItem::Name(name)
130 }
131 },
132 );
133
134 if inner_stack_effect.parse::<Token![,]>().is_err() {
135 break;
136 }
137 }
138
139 Ok(Opcode {
140 name,
141 number,
142 stack_effect: Some(stack_effect),
143 })
144 }
145}
146
147impl Parse for Exception {
148 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
149 input.parse::<Token![*]>()?;
153 syn::custom_keyword!(EXCEPTION);
154 input.parse::<EXCEPTION>()?;
155
156 let inner_stack_effect;
157
158 parenthesized!(inner_stack_effect in input);
159
160 let mut stack_effect = StackEffect {
161 pops: vec![],
162 pushes: vec![],
163 };
164
165 while inner_stack_effect.peek(Ident) {
167 let name: Ident = inner_stack_effect.parse()?;
168
169 stack_effect.pops.push(
170 if name == "unused" {
172 if inner_stack_effect.peek(syn::token::Bracket) {
173 let inner_bracket;
174 bracketed!(inner_bracket in inner_stack_effect);
175 let size: Expr = inner_bracket.parse()?;
176 StackItem::Unused(size)
177 } else {
178 StackItem::Unused(Expr::Lit(syn::ExprLit {
179 attrs: vec![],
180 lit: syn::Lit::Int(LitInt::new(
181 "1",
182 proc_macro::Span::call_site().into(),
183 )),
184 }))
185 }
186 } else {
187 if inner_stack_effect.peek(syn::token::Bracket) {
188 let inner_bracket;
189 bracketed!(inner_bracket in inner_stack_effect);
190 let size: Expr = inner_bracket.parse()?;
191 StackItem::NameCounted(name, size)
192 } else {
193 StackItem::Name(name)
194 }
195 },
196 );
197
198 if inner_stack_effect.parse::<Token![,]>().is_err() {
199 break;
200 }
201 }
202
203 inner_stack_effect.parse::<Token![-]>()?;
204 inner_stack_effect.parse::<Token![-]>()?;
205
206 while inner_stack_effect.peek(Ident) {
207 let name: Ident = inner_stack_effect.parse()?;
208
209 stack_effect.pushes.push(
210 if name == "unused" {
212 if inner_stack_effect.peek(syn::token::Bracket) {
213 let inner_bracket;
214 bracketed!(inner_bracket in inner_stack_effect);
215 let size: Expr = inner_bracket.parse()?;
216 StackItem::Unused(size)
217 } else {
218 StackItem::Unused(Expr::Lit(syn::ExprLit {
219 attrs: vec![],
220 lit: syn::Lit::Int(LitInt::new(
221 "1",
222 proc_macro::Span::call_site().into(),
223 )),
224 }))
225 }
226 } else {
227 if inner_stack_effect.peek(syn::token::Bracket) {
228 let inner_bracket;
229 bracketed!(inner_bracket in inner_stack_effect);
230 let size: Expr = inner_bracket.parse()?;
231 StackItem::NameCounted(name, size)
232 } else {
233 StackItem::Name(name)
234 }
235 },
236 );
237
238 if inner_stack_effect.parse::<Token![,]>().is_err() {
239 break;
240 }
241 }
242
243 Ok(Exception { stack_effect })
244 }
245}
246
247struct Opcodes {
248 opcodes: Vec<Opcode>,
249 exception: Option<Exception>,
250}
251
252impl Parse for Opcodes {
253 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
254 let mut opcodes = vec![];
255 let mut exception = None;
256
257 loop {
258 if input.peek(Token![*]) {
259 exception = Some(Exception::parse(input)?);
260 } else {
261 opcodes.push(Opcode::parse(input)?);
262 }
263
264 if input.parse::<Token![,]>().is_err() || input.is_empty() {
265 break;
266 }
267 }
268
269 Ok(Opcodes { opcodes, exception })
270 }
271}
272
273fn sum_items(items: &[StackItem]) -> Expr {
274 if items.is_empty() {
275 Expr::Lit(syn::ExprLit {
277 attrs: vec![],
278 lit: syn::Lit::Int(LitInt::new("0", proc_macro::Span::call_site().into())),
279 })
280 } else {
281 items
282 .iter()
283 .map(|p| match p {
284 StackItem::Name(_) => Expr::Lit(syn::ExprLit {
285 attrs: vec![],
286 lit: syn::Lit::Int(LitInt::new("1", proc_macro::Span::call_site().into())),
287 }),
288 StackItem::NameCounted(_, size) => size.clone(),
289 StackItem::Unused(size) => size.clone(),
290 })
291 .reduce(|left, right| {
292 syn::Expr::Binary(syn::ExprBinary {
293 attrs: vec![],
294 left: Box::new(left),
295 op: syn::BinOp::Add(syn::token::Plus {
296 spans: [proc_macro::Span::call_site().into()],
297 }),
298 right: Box::new(right),
299 })
300 })
301 .expect("Something is wrong with the format")
302 }
303}
304
305fn collect_stack_effect<'a, T>(
306 stack_items: T,
307 index_offset: Option<proc_macro2::TokenStream>,
308) -> (Vec<proc_macro2::TokenStream>, proc_macro2::TokenStream)
309where
310 T: DoubleEndedIterator<Item = &'a StackItem>,
311{
312 let mut index = if let Some(ref index_offset) = index_offset {
313 index_offset.clone()
314 } else {
315 quote! { 0 }
316 };
317
318 let mut prev_index = index.clone();
319
320 let mut fields = vec![];
321
322 for item in stack_items.rev() {
323 prev_index = index.clone();
324 let count = match item {
325 StackItem::Name(name) => {
326 let name = name.to_string();
327
328 let new_index = if index_offset.is_none() {
329 quote! { (#index) - 1 }
330 } else {
331 index.clone()
332 };
333
334 fields.push(quote! { StackItem { name: #name, count: 1, index: #new_index } });
335 quote! { 1 }
336 }
337 StackItem::NameCounted(name, count) => {
338 let name = name.to_string();
339
340 let new_index = if index_offset.is_none() {
341 quote! { (#index) - (#count) }
342 } else {
343 index.clone()
344 };
345
346 fields.push(
347 quote! { StackItem { name: #name, count: (#count) as u32, index: #new_index } },
348 );
349 quote! { #count }
350 }
351 StackItem::Unused(count) => quote! { #count },
352 };
353
354 if index_offset.is_none() {
355 index = quote! { (#index) - (#count) };
356 } else {
357 index = quote! { (#index) + (#count) };
358 }
359 }
360
361 if index_offset.is_none() {
362 fields.reverse();
363 }
364
365 (fields, index)
366}
367
368#[proc_macro]
369pub fn define_opcodes(input: TokenStream) -> TokenStream {
370 let Opcodes { opcodes, exception } = parse_macro_input!(input as Opcodes);
371
372 let opcodes_with_stack: Vec<_> = opcodes
373 .iter()
374 .filter(|o| o.stack_effect.is_some())
375 .collect();
376
377 let names: Vec<_> = opcodes.iter().map(|o| &o.name).collect();
378 let camel_names: Vec<Ident> = names
379 .iter()
380 .map(|ident| {
381 let camel = ident.to_string().to_upper_camel_case();
382 Ident::new(&camel, ident.span())
383 })
384 .collect();
385
386 let names_with_stack: Vec<_> = opcodes_with_stack.iter().map(|o| &o.name).collect();
387
388 let numbers: Vec<_> = opcodes.iter().map(|o| &o.number).collect();
389
390 let pops: Vec<_> = opcodes_with_stack
391 .iter()
392 .map(|o| sum_items(&o.stack_effect.as_ref().unwrap().pops))
393 .collect();
394
395 let pushes: Vec<_> = opcodes_with_stack
396 .iter()
397 .map(|o| sum_items(&o.stack_effect.as_ref().unwrap().pushes))
398 .collect();
399
400 let mut expanded = quote! {
401 #[allow(non_camel_case_types)]
402 #[allow(clippy::upper_case_acronyms)]
403 #[derive(Debug, Clone, PartialEq, Eq)]
404 pub enum Opcode {
405 #( #names ),*,
406 INVALID_OPCODE(u8),
407 }
408
409 impl From<u8> for Opcode {
410 fn from(value: u8) -> Self {
411 match value {
412 #( #numbers => Opcode::#names, )*
413 _ => Opcode::INVALID_OPCODE(value),
414 }
415 }
416 }
417
418 impl From<Opcode> for u8 {
419 fn from(value: Opcode) -> Self {
420 match value {
421 #( Opcode::#names => #numbers , )*
422 Opcode::INVALID_OPCODE(value) => value,
423 }
424 }
425 }
426
427 impl From<(Opcode, u8)> for Instruction {
428 fn from(value: (Opcode, u8)) -> Self {
429 match value.0 {
430 #(
431 Opcode::#names => Instruction::#camel_names(value.1),
432 )*
433 Opcode::INVALID_OPCODE(opcode) => {
434 if !cfg!(test) {
435 Instruction::InvalidOpcode((opcode, value.1))
436 } else {
437 panic!("Testing environment should not come across invalid opcodes")
438 }
439 },
440 }
441 }
442 }
443
444 impl Opcode {
445 pub fn from_instruction(instruction: &Instruction) -> Self {
446 match instruction {
447 #(
448 Instruction::#camel_names(_) => Opcode::#names ,
449 )*
450 Instruction::InvalidOpcode((opcode, _)) => Opcode::INVALID_OPCODE(*opcode),
451 }
452 }
453 }
454
455 impl StackEffectTrait for Opcode {
456 fn stack_effect(&self, oparg: u32, jump: bool, calculate_max: bool) -> StackEffect {
457 match &self {
458 #(
459 Opcode::#names_with_stack => StackEffect { pops: #pops, pushes: #pushes },
460 )*
461 Opcode::INVALID_OPCODE(_) => StackEffect { pops: 0, pushes: 0 },
462
463 _ => unimplemented!("stack_effect not implemented for {:?}", self),
464 }
465 }
466 }
467 };
468
469 let mut input_sirs = vec![];
470 let mut output_sirs = vec![];
471 let mut stack_deltas = vec![];
472
473 for (opcode, name) in opcodes.iter().zip(names) {
474 let mut input_constructor_fields = vec![];
475 let mut output_constructor_fields = vec![];
476 let mut stack_delta = quote! { 0 };
477
478 if let Some(stack_effect) = &opcode.stack_effect {
479 let input_offset;
480 (input_constructor_fields, input_offset) =
481 collect_stack_effect(stack_effect.pops.iter(), None);
482
483 (output_constructor_fields, _) =
484 collect_stack_effect(stack_effect.pushes.iter().rev(), Some(input_offset));
485
486 let pushes = sum_items(&stack_effect.pushes);
487 let pops = sum_items(&stack_effect.pops);
488
489 stack_delta = quote! { (#pushes) as isize - (#pops) as isize};
490 }
491
492 input_sirs.push(quote! { Opcode::#name => vec![
493 #(
494 #input_constructor_fields
495 ),*
496 ] });
497
498 output_sirs.push(quote! { Opcode::#name => vec![
499 #(
500 #output_constructor_fields
501 ),*
502 ] });
503
504 stack_deltas.push(quote! { Opcode::#name => #stack_delta });
505 }
506
507 let sir_exception = if let Some(exception) = exception {
508 let (input_fields, input_offset) =
509 collect_stack_effect(exception.stack_effect.pops.iter(), None);
510
511 let (output_fields, _) = collect_stack_effect(
512 exception.stack_effect.pushes.iter().rev(),
513 Some(input_offset),
514 );
515
516 let pushes = sum_items(&exception.stack_effect.pushes);
517 let pops = sum_items(&exception.stack_effect.pops);
518
519 let stack_delta = quote! { (#pushes) as isize - (#pops) as isize};
520
521 quote! {
522 #[derive(PartialEq, Debug, Clone)]
523 pub struct SIRException {
524 pub lasti: bool,
525 pub stack_depth: usize,
526 pub input: Vec<StackItem>,
527 pub output: Vec<StackItem>,
528 pub net_stack_delta: isize
529 }
530
531 impl SIRException {
532 pub fn new(lasti: bool, stack_depth: usize, jump: bool) -> Self {
533 let input = vec![
534 #(
535 #input_fields
536 ),*
537 ];
538
539 let output = vec![
540 #(
541 #output_fields
542 ),*
543 ];
544
545 let net_stack_delta = #stack_delta;
546
547 Self {
548 lasti,
549 stack_depth,
550 input,
551 output,
552 net_stack_delta,
553 }
554 }
555 }
556
557 impl GenericSIRException for SIRException {
558 type Opcode = Opcode;
559
560 fn new(lasti: bool, stack_depth: usize, jump: bool) -> Self {
561 SIRException::new(lasti, stack_depth, jump)
562 }
563
564 fn get_outputs(&self) -> &[StackItem] {
565 &self.output
566 }
567
568 fn get_inputs(&self) -> &[StackItem] {
569 &self.input
570 }
571
572 fn get_net_stack_delta(&self) -> isize {
573 self.net_stack_delta
574 }
575
576 fn get_stack_depth(&self) -> usize {
577 self.stack_depth
578 }
579 }
580
581 impl std::fmt::Display for ExceptionCall<SIRNode> {
582 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
583 let mut inputs = self
584 .stack_inputs
585 .iter()
586 .map(|input| format!("{}", input))
587 .collect::<Vec<_>>();
588
589 inputs.push(format!("{}", self.exception.lasti));
590
591 write!(f, "EXCEPTION({})", inputs.join(", "))
592 }
593 }
594 }
595 } else {
596 quote! {#[derive(PartialEq, Debug, Clone)]
597 pub struct SIRException {
600 }
601
602 impl std::fmt::Display for ExceptionCall<SIRNode> {
603 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
604 unreachable!()
605 }
606 }
607
608 impl GenericSIRException for SIRException {
609 type Opcode = Opcode;
610
611 fn new(lasti: bool, stack_depth: usize, jump: bool) -> Self {
612 SIRException {}
613 }
614
615 fn get_outputs(&self) -> &[StackItem] {
616 &[]
617 }
618
619 fn get_inputs(&self) -> &[StackItem] {
620 &[]
621 }
622
623 fn get_net_stack_delta(&self) -> isize {
624 0
625 }
626
627 fn get_stack_depth(&self) -> usize {
628 0
629 }
630 }
631 }
632 };
633
634 let sir = quote! {
635 #[cfg(feature = "sir")]
636 pub mod sir {
637 use super::{Opcode};
638 use crate::sir::{SIR, StackItem, SIRStatement, ExceptionCall, Call, SIRExpression, AuxVar};
639 use crate::traits::{GenericSIRNode, SIROwned, GenericSIRException};
640
641
642 #[derive(PartialEq, Debug, Clone)]
643 pub struct SIRNode {
644 pub opcode: Opcode,
645 pub oparg: u32,
646 pub input: Vec<StackItem>,
647 pub output: Vec<StackItem>,
648 pub net_stack_delta: isize
649 }
650
651 impl SIRNode {
652 pub fn new(opcode: Opcode, oparg: u32, jump: bool) -> Self {
653 let calculate_max = false;
655 let oparg = oparg as isize;
656
657 let input = match opcode {
658 #(
659 #input_sirs
660 ),*,
661 Opcode::INVALID_OPCODE(_) => vec![],
662 };
663
664 let output = match opcode {
665 #(
666 #output_sirs
667 ),*,
668 Opcode::INVALID_OPCODE(_) => vec![],
669 };
670
671 let net_stack_delta = match opcode {
672 #(
673 #stack_deltas
674 ),*,
675 Opcode::INVALID_OPCODE(_) => 0,
676 };
677
678 Self {
679 opcode,
680 oparg: oparg as u32,
681 input,
682 output,
683 net_stack_delta
684 }
685 }
686 }
687
688 #sir_exception
689
690 impl GenericSIRNode for SIRNode {
691 type Opcode = Opcode;
692 type SIRException = SIRException;
693
694 fn new(opcode: Self::Opcode, oparg: u32, jump: bool) -> Self {
695 SIRNode::new(opcode, oparg, jump)
696 }
697
698 fn get_outputs(&self) -> &[StackItem] {
699 &self.output
700 }
701
702 fn get_inputs(&self) -> &[StackItem] {
703 &self.input
704 }
705
706 fn get_net_stack_delta(&self) -> isize {
707 self.net_stack_delta
708 }
709 }
710
711 impl SIROwned<SIRNode> for SIR<SIRNode> {
712 fn new(statements: Vec<SIRStatement<SIRNode>>) -> Self {
713 SIR(statements)
714 }
715 }
716
717 impl std::fmt::Display for SIR<SIRNode> {
718 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
719 for statement in &self.0 {
720 match statement {
721 SIRStatement::Assignment(aux_var, call) => {
722 writeln!(f, "{} = {}", aux_var.name, call)?;
723 }
724 SIRStatement::TupleAssignment(aux_vars, call) => {
725 let vars = aux_vars.iter().map(|v| v.name.clone()).collect::<Vec<_>>().join(", ");
726 writeln!(f, "({}) = {}", vars, call)?;
727 }
728 SIRStatement::DisregardCall(call) => {
729 writeln!(f, "{}", call)?;
730 }
731 }
732 }
733 Ok(())
734 }
735 }
736
737 impl std::fmt::Display for Call<SIRNode> {
738 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
739 let mut inputs = self
740 .stack_inputs
741 .iter()
742 .map(|input| format!("{}", input))
743 .collect::<Vec<_>>();
744
745 inputs.push(format!("{}", self.node.oparg));
746
747 write!(f, "{:#?}({})", self.node.opcode, inputs.join(", "))
748 }
749 }
750
751 impl std::fmt::Display for SIRExpression<SIRNode> {
752 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
753 match self {
754 SIRExpression::Call(call) => write!(f, "{}", call),
755 SIRExpression::Exception(exception_call) => write!(f, "{}", exception_call),
756 SIRExpression::AuxVar(aux_var) => write!(f, "{}", aux_var.name.clone()),
757 SIRExpression::PhiNode(phi) => write!(f, "phi({})", phi.iter().map(|v| &v.name).cloned().collect::<Vec<_>>().join(", ")),
758 SIRExpression::GeneratorStart => write!(f, "GenStart()"),
759 }
760 }
761 }
762 }
763 };
764
765 expanded.extend(sir);
766
767 expanded.into()
768}
769
770#[cfg(test)]
771mod tests {
772 use proc_macro2::Span;
773 use syn::Ident;
774
775 use crate::StackItem;
776
777 #[test]
778 fn test_stack_effect() {
779 let inputs = [
780 StackItem::Name(Ident::new("first", Span::call_site())),
781 StackItem::Name(Ident::new("second", Span::call_site())),
782 ];
783
784 let outputs = [
785 StackItem::Name(Ident::new("out", Span::call_site())),
786 StackItem::Name(Ident::new("out2", Span::call_site())),
787 ];
788
789 let (input_fields, input_offset) = crate::collect_stack_effect(inputs.iter(), None);
790
791 let (output_fields, _) =
792 crate::collect_stack_effect(outputs.iter().rev(), Some(input_offset));
793
794 for input_field in input_fields {
795 println!("{}", input_field);
796 }
797
798 assert_eq!(
799 format!("{}", output_fields[0]),
800 "StackItem { name : \"out\" , count : 1 , index : ((0) - (1)) - (1) }"
801 );
802
803 assert_eq!(
804 format!("{}", output_fields[1]),
805 "StackItem { name : \"out2\" , count : 1 , index : (((0) - (1)) - (1)) + (1) }"
806 );
807
808 for output_field in output_fields {
809 println!("{}", output_field);
810 }
811 }
812
813 #[test]
814 fn test_stack_effect2() {
815 let inputs = [
816 StackItem::NameCounted(
817 Ident::new("first", Span::call_site()),
818 syn::Expr::Lit(syn::ExprLit {
819 attrs: vec![],
820 lit: syn::Lit::Int(syn::LitInt::new("5", Span::call_site().into())),
821 }),
822 ),
823 StackItem::Name(Ident::new("second", Span::call_site())),
824 ];
825
826 let outputs = [
827 StackItem::NameCounted(
828 Ident::new("out", Span::call_site()),
829 syn::Expr::Lit(syn::ExprLit {
830 attrs: vec![],
831 lit: syn::Lit::Int(syn::LitInt::new("5", Span::call_site().into())),
832 }),
833 ),
834 StackItem::Name(Ident::new("out2", Span::call_site())),
835 ];
836
837 let (input_fields, input_offset) = crate::collect_stack_effect(inputs.iter(), None);
838
839 let (output_fields, _) =
840 crate::collect_stack_effect(outputs.iter().rev(), Some(input_offset));
841
842 for input_field in input_fields {
843 println!("{}", input_field);
844 }
845
846 assert_eq!(
847 format!("{}", output_fields[0]),
848 "StackItem { name : \"out\" , count : (5) as u32 , index : ((0) - (1)) - (5) }"
849 );
850
851 assert_eq!(
852 format!("{}", output_fields[1]),
853 "StackItem { name : \"out2\" , count : 1 , index : (((0) - (1)) - (5)) + (5) }"
854 );
855
856 for output_field in output_fields {
857 println!("{}", output_field);
858 }
859 }
860
861 #[test]
862 fn test_stack_effect_copy() {
863 let inputs = [
865 StackItem::Name(Ident::new("bottom", Span::call_site())),
866 StackItem::Unused(syn::Expr::Lit(syn::ExprLit {
867 attrs: vec![],
868 lit: syn::Lit::Int(syn::LitInt::new("2", Span::call_site().into())),
869 })),
870 ];
871
872 let outputs = [
873 StackItem::Name(Ident::new("bottom", Span::call_site())),
874 StackItem::Unused(syn::Expr::Lit(syn::ExprLit {
875 attrs: vec![],
876 lit: syn::Lit::Int(syn::LitInt::new("2", Span::call_site().into())),
877 })),
878 StackItem::Name(Ident::new("top", Span::call_site())),
879 ];
880
881 let (input_fields, input_offset) = crate::collect_stack_effect(inputs.iter(), None);
882
883 let (output_fields, _) =
884 crate::collect_stack_effect(outputs.iter().rev(), Some(input_offset));
885
886 for input_field in input_fields {
887 println!("{}", input_field);
888 }
889
890 assert_eq!(
891 format!("{}", output_fields[0]),
892 "StackItem { name : \"bottom\" , count : 1 , index : ((0) - (2)) - (1) }"
893 );
894
895 assert_eq!(
896 format!("{}", output_fields[1]),
897 "StackItem { name : \"top\" , count : 1 , index : ((((0) - (2)) - (1)) + (1)) + (2) }"
898 );
899
900 for output_field in output_fields {
901 println!("{}", output_field);
902 }
903 }
904}