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 fields = vec![];
319
320 for item in stack_items.rev() {
321 let count = match item {
322 StackItem::Name(name) => {
323 let name = name.to_string();
324
325 let new_index = if index_offset.is_none() {
326 quote! { (#index) - 1 }
327 } else {
328 index.clone()
329 };
330
331 fields.push(quote! { StackItem { name: #name, count: 1, index: #new_index } });
332 quote! { 1 }
333 }
334 StackItem::NameCounted(name, count) => {
335 let name = name.to_string();
336
337 let new_index = if index_offset.is_none() {
338 quote! { (#index) - (#count) }
339 } else {
340 index.clone()
341 };
342
343 fields.push(
344 quote! { StackItem { name: #name, count: (#count) as u32, index: #new_index } },
345 );
346 quote! { #count }
347 }
348 StackItem::Unused(count) => quote! { #count },
349 };
350
351 if index_offset.is_none() {
352 index = quote! { (#index) - (#count) };
353 } else {
354 index = quote! { (#index) + (#count) };
355 }
356 }
357
358 if index_offset.is_none() {
359 fields.reverse();
360 }
361
362 (fields, index)
363}
364
365#[proc_macro]
366pub fn define_opcodes(input: TokenStream) -> TokenStream {
367 let Opcodes { opcodes, exception } = parse_macro_input!(input as Opcodes);
368
369 let opcodes_with_stack: Vec<_> = opcodes
370 .iter()
371 .filter(|o| o.stack_effect.is_some())
372 .collect();
373
374 let names: Vec<_> = opcodes.iter().map(|o| &o.name).collect();
375 let camel_names: Vec<Ident> = names
376 .iter()
377 .map(|ident| {
378 let camel = ident.to_string().to_upper_camel_case();
379 Ident::new(&camel, ident.span())
380 })
381 .collect();
382
383 let names_with_stack: Vec<_> = opcodes_with_stack.iter().map(|o| &o.name).collect();
384
385 let numbers: Vec<_> = opcodes.iter().map(|o| &o.number).collect();
386
387 let pops: Vec<_> = opcodes_with_stack
388 .iter()
389 .map(|o| sum_items(&o.stack_effect.as_ref().unwrap().pops))
390 .collect();
391
392 let pushes: Vec<_> = opcodes_with_stack
393 .iter()
394 .map(|o| sum_items(&o.stack_effect.as_ref().unwrap().pushes))
395 .collect();
396
397 let mut expanded = quote! {
398 #[allow(non_camel_case_types)]
399 #[allow(clippy::upper_case_acronyms)]
400 #[derive(Debug, Clone, PartialEq, Eq)]
401 pub enum Opcode {
402 #( #names ),*,
403 INVALID_OPCODE(u8),
404 }
405
406 impl From<u8> for Opcode {
407 fn from(value: u8) -> Self {
408 match value {
409 #( #numbers => Opcode::#names, )*
410 _ => Opcode::INVALID_OPCODE(value),
411 }
412 }
413 }
414
415 impl From<Opcode> for u8 {
416 fn from(value: Opcode) -> Self {
417 match value {
418 #( Opcode::#names => #numbers , )*
419 Opcode::INVALID_OPCODE(value) => value,
420 }
421 }
422 }
423
424 impl From<(Opcode, u8)> for Instruction {
425 fn from(value: (Opcode, u8)) -> Self {
426 match value.0 {
427 #(
428 Opcode::#names => Instruction::#camel_names(value.1),
429 )*
430 Opcode::INVALID_OPCODE(opcode) => {
431 if !cfg!(test) {
432 Instruction::InvalidOpcode((opcode, value.1))
433 } else {
434 panic!("Testing environment should not come across invalid opcodes")
435 }
436 },
437 }
438 }
439 }
440
441 impl Opcode {
442 pub fn from_instruction(instruction: &Instruction) -> Self {
443 match instruction {
444 #(
445 Instruction::#camel_names(_) => Opcode::#names ,
446 )*
447 Instruction::InvalidOpcode((opcode, _)) => Opcode::INVALID_OPCODE(*opcode),
448 }
449 }
450 }
451
452 impl StackEffectTrait for Opcode {
453 fn stack_effect(&self, oparg: u32, jump: bool, calculate_max: bool) -> StackEffect {
454 match &self {
455 #(
456 Opcode::#names_with_stack => StackEffect { pops: #pops, pushes: #pushes },
457 )*
458 Opcode::INVALID_OPCODE(_) => StackEffect { pops: 0, pushes: 0 },
459
460 _ => unimplemented!("stack_effect not implemented for {:?}", self),
461 }
462 }
463 }
464 };
465
466 let mut input_sirs = vec![];
467 let mut output_sirs = vec![];
468 let mut stack_deltas = vec![];
469
470 for (opcode, name) in opcodes.iter().zip(names) {
471 let mut input_constructor_fields = vec![];
472 let mut output_constructor_fields = vec![];
473 let mut stack_delta = quote! { 0 };
474
475 if let Some(stack_effect) = &opcode.stack_effect {
476 let input_offset;
477 (input_constructor_fields, input_offset) =
478 collect_stack_effect(stack_effect.pops.iter(), None);
479
480 (output_constructor_fields, _) =
481 collect_stack_effect(stack_effect.pushes.iter().rev(), Some(input_offset));
482
483 let pushes = sum_items(&stack_effect.pushes);
484 let pops = sum_items(&stack_effect.pops);
485
486 stack_delta = quote! { (#pushes) as isize - (#pops) as isize};
487 }
488
489 input_sirs.push(quote! { Opcode::#name => vec![
490 #(
491 #input_constructor_fields
492 ),*
493 ] });
494
495 output_sirs.push(quote! { Opcode::#name => vec![
496 #(
497 #output_constructor_fields
498 ),*
499 ] });
500
501 stack_deltas.push(quote! { Opcode::#name => #stack_delta });
502 }
503
504 let sir_exception = if let Some(exception) = exception {
505 let (input_fields, input_offset) =
506 collect_stack_effect(exception.stack_effect.pops.iter(), None);
507
508 let (output_fields, _) = collect_stack_effect(
509 exception.stack_effect.pushes.iter().rev(),
510 Some(input_offset),
511 );
512
513 let pushes = sum_items(&exception.stack_effect.pushes);
514 let pops = sum_items(&exception.stack_effect.pops);
515
516 let stack_delta = quote! { (#pushes) as isize - (#pops) as isize};
517
518 quote! {
519 #[derive(PartialEq, Debug, Clone)]
520 pub struct SIRException {
521 pub lasti: bool,
522 pub stack_depth: usize,
523 pub input: Vec<StackItem>,
524 pub output: Vec<StackItem>,
525 pub net_stack_delta: isize
526 }
527
528 impl SIRException {
529 pub fn new(lasti: bool, stack_depth: usize, jump: bool) -> Self {
530 let input = vec![
531 #(
532 #input_fields
533 ),*
534 ];
535
536 let output = vec![
537 #(
538 #output_fields
539 ),*
540 ];
541
542 let net_stack_delta = #stack_delta;
543
544 Self {
545 lasti,
546 stack_depth,
547 input,
548 output,
549 net_stack_delta,
550 }
551 }
552 }
553
554 impl GenericSIRException for SIRException {
555 type Opcode = Opcode;
556
557 fn new(lasti: bool, stack_depth: usize, jump: bool) -> Self {
558 SIRException::new(lasti, stack_depth, jump)
559 }
560
561 fn get_outputs(&self) -> &[StackItem] {
562 &self.output
563 }
564
565 fn get_inputs(&self) -> &[StackItem] {
566 &self.input
567 }
568
569 fn get_net_stack_delta(&self) -> isize {
570 self.net_stack_delta
571 }
572
573 fn get_stack_depth(&self) -> usize {
574 self.stack_depth
575 }
576 }
577
578 impl std::fmt::Display for ExceptionCall<SIRNode> {
579 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
580 let mut inputs = self
581 .stack_inputs
582 .iter()
583 .map(|input| format!("{}", input))
584 .collect::<Vec<_>>();
585
586 inputs.push(format!("{}", self.exception.lasti));
587
588 write!(f, "EXCEPTION({})", inputs.join(", "))
589 }
590 }
591 }
592 } else {
593 quote! {#[derive(PartialEq, Debug, Clone)]
594 pub struct SIRException {
597 }
598
599 impl std::fmt::Display for ExceptionCall<SIRNode> {
600 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
601 unreachable!()
602 }
603 }
604
605 impl GenericSIRException for SIRException {
606 type Opcode = Opcode;
607
608 fn new(lasti: bool, stack_depth: usize, jump: bool) -> Self {
609 SIRException {}
610 }
611
612 fn get_outputs(&self) -> &[StackItem] {
613 &[]
614 }
615
616 fn get_inputs(&self) -> &[StackItem] {
617 &[]
618 }
619
620 fn get_net_stack_delta(&self) -> isize {
621 0
622 }
623
624 fn get_stack_depth(&self) -> usize {
625 0
626 }
627 }
628 }
629 };
630
631 let sir = quote! {
632 #[cfg(feature = "sir")]
633 pub mod sir {
634 use super::{Opcode};
635 use crate::sir::{SIR, StackItem, SIRStatement, ExceptionCall, Call, SIRExpression, AuxVar};
636 use crate::traits::{GenericSIRNode, SIROwned, GenericSIRException};
637
638
639 #[derive(PartialEq, Debug, Clone)]
640 pub struct SIRNode {
641 pub opcode: Opcode,
642 pub oparg: u32,
643 pub input: Vec<StackItem>,
644 pub output: Vec<StackItem>,
645 pub net_stack_delta: isize
646 }
647
648 impl SIRNode {
649 pub fn new(opcode: Opcode, oparg: u32, jump: bool) -> Self {
650 let calculate_max = false;
652 let oparg = oparg as isize;
653
654 let input = match opcode {
655 #(
656 #input_sirs
657 ),*,
658 Opcode::INVALID_OPCODE(_) => vec![],
659 };
660
661 let output = match opcode {
662 #(
663 #output_sirs
664 ),*,
665 Opcode::INVALID_OPCODE(_) => vec![],
666 };
667
668 let net_stack_delta = match opcode {
669 #(
670 #stack_deltas
671 ),*,
672 Opcode::INVALID_OPCODE(_) => 0,
673 };
674
675 Self {
676 opcode,
677 oparg: oparg as u32,
678 input,
679 output,
680 net_stack_delta
681 }
682 }
683 }
684
685 #sir_exception
686
687 impl GenericSIRNode for SIRNode {
688 type Opcode = Opcode;
689 type SIRException = SIRException;
690
691 fn new(opcode: Self::Opcode, oparg: u32, jump: bool) -> Self {
692 SIRNode::new(opcode, oparg, jump)
693 }
694
695 fn get_outputs(&self) -> &[StackItem] {
696 &self.output
697 }
698
699 fn get_inputs(&self) -> &[StackItem] {
700 &self.input
701 }
702
703 fn get_net_stack_delta(&self) -> isize {
704 self.net_stack_delta
705 }
706 }
707
708 impl SIROwned<SIRNode> for SIR<SIRNode> {
709 fn new(statements: Vec<SIRStatement<SIRNode>>) -> Self {
710 SIR(statements)
711 }
712 }
713
714 impl std::fmt::Display for SIR<SIRNode> {
715 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
716 for statement in &self.0 {
717 match statement {
718 SIRStatement::Assignment(aux_var, call) => {
719 writeln!(f, "{} = {}", aux_var.name, call)?;
720 }
721 SIRStatement::TupleAssignment(aux_vars, call) => {
722 let vars = aux_vars.iter().map(|v| v.name.clone()).collect::<Vec<_>>().join(", ");
723 writeln!(f, "({}) = {}", vars, call)?;
724 }
725 SIRStatement::DisregardCall(call) => {
726 writeln!(f, "{}", call)?;
727 }
728 SIRStatement::UseVar(aux_var) => {
729 write!(f, "UseVar({})", aux_var.name.clone())?;
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::PhiNode(phi) => write!(f, "phi({})", phi.iter().map(|v| &v.name).cloned().collect::<Vec<_>>().join(", ")),
757 SIRExpression::GeneratorStart => write!(f, "GenStart()"),
758 }
759 }
760 }
761 }
762 };
763
764 expanded.extend(sir);
765
766 expanded.into()
767}
768
769#[cfg(test)]
770mod tests {
771 use proc_macro2::Span;
772 use syn::Ident;
773
774 use crate::StackItem;
775
776 #[test]
777 fn test_stack_effect() {
778 let inputs = [
779 StackItem::Name(Ident::new("first", Span::call_site())),
780 StackItem::Name(Ident::new("second", Span::call_site())),
781 ];
782
783 let outputs = [
784 StackItem::Name(Ident::new("out", Span::call_site())),
785 StackItem::Name(Ident::new("out2", Span::call_site())),
786 ];
787
788 let (input_fields, input_offset) = crate::collect_stack_effect(inputs.iter(), None);
789
790 let (output_fields, _) =
791 crate::collect_stack_effect(outputs.iter().rev(), Some(input_offset));
792
793 for input_field in input_fields {
794 println!("{}", input_field);
795 }
796
797 assert_eq!(
798 format!("{}", output_fields[0]),
799 "StackItem { name : \"out\" , count : 1 , index : ((0) - (1)) - (1) }"
800 );
801
802 assert_eq!(
803 format!("{}", output_fields[1]),
804 "StackItem { name : \"out2\" , count : 1 , index : (((0) - (1)) - (1)) + (1) }"
805 );
806
807 for output_field in output_fields {
808 println!("{}", output_field);
809 }
810 }
811
812 #[test]
813 fn test_stack_effect2() {
814 let inputs = [
815 StackItem::NameCounted(
816 Ident::new("first", Span::call_site()),
817 syn::Expr::Lit(syn::ExprLit {
818 attrs: vec![],
819 lit: syn::Lit::Int(syn::LitInt::new("5", Span::call_site().into())),
820 }),
821 ),
822 StackItem::Name(Ident::new("second", Span::call_site())),
823 ];
824
825 let outputs = [
826 StackItem::NameCounted(
827 Ident::new("out", Span::call_site()),
828 syn::Expr::Lit(syn::ExprLit {
829 attrs: vec![],
830 lit: syn::Lit::Int(syn::LitInt::new("5", Span::call_site().into())),
831 }),
832 ),
833 StackItem::Name(Ident::new("out2", Span::call_site())),
834 ];
835
836 let (input_fields, input_offset) = crate::collect_stack_effect(inputs.iter(), None);
837
838 let (output_fields, _) =
839 crate::collect_stack_effect(outputs.iter().rev(), Some(input_offset));
840
841 for input_field in input_fields {
842 println!("{}", input_field);
843 }
844
845 assert_eq!(
846 format!("{}", output_fields[0]),
847 "StackItem { name : \"out\" , count : (5) as u32 , index : ((0) - (1)) - (5) }"
848 );
849
850 assert_eq!(
851 format!("{}", output_fields[1]),
852 "StackItem { name : \"out2\" , count : 1 , index : (((0) - (1)) - (5)) + (5) }"
853 );
854
855 for output_field in output_fields {
856 println!("{}", output_field);
857 }
858 }
859
860 #[test]
861 fn test_stack_effect_copy() {
862 let inputs = [
864 StackItem::Name(Ident::new("bottom", Span::call_site())),
865 StackItem::Unused(syn::Expr::Lit(syn::ExprLit {
866 attrs: vec![],
867 lit: syn::Lit::Int(syn::LitInt::new("2", Span::call_site().into())),
868 })),
869 ];
870
871 let outputs = [
872 StackItem::Name(Ident::new("bottom", Span::call_site())),
873 StackItem::Unused(syn::Expr::Lit(syn::ExprLit {
874 attrs: vec![],
875 lit: syn::Lit::Int(syn::LitInt::new("2", Span::call_site().into())),
876 })),
877 StackItem::Name(Ident::new("top", Span::call_site())),
878 ];
879
880 let (input_fields, input_offset) = crate::collect_stack_effect(inputs.iter(), None);
881
882 let (output_fields, _) =
883 crate::collect_stack_effect(outputs.iter().rev(), Some(input_offset));
884
885 for input_field in input_fields {
886 println!("{}", input_field);
887 }
888
889 assert_eq!(
890 format!("{}", output_fields[0]),
891 "StackItem { name : \"bottom\" , count : 1 , index : ((0) - (2)) - (1) }"
892 );
893
894 assert_eq!(
895 format!("{}", output_fields[1]),
896 "StackItem { name : \"top\" , count : 1 , index : ((((0) - (2)) - (1)) + (1)) + (2) }"
897 );
898
899 for output_field in output_fields {
900 println!("{}", output_field);
901 }
902 }
903}