github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/compiler/struct_test.go (about)

     1  package compiler_test
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"math/big"
     7  	"strings"
     8  	"testing"
     9  
    10  	"github.com/nspcc-dev/neo-go/pkg/compiler"
    11  	"github.com/nspcc-dev/neo-go/pkg/vm"
    12  	"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
    13  	"github.com/stretchr/testify/require"
    14  )
    15  
    16  var structTestCases = []testCase{
    17  	{
    18  		"struct field assign",
    19  		`func F%d() int {
    20  			t := token1 {
    21  				x: 2,
    22  				y: 4,
    23  			}
    24  
    25  			age := t.x
    26  			return age
    27  		}
    28  
    29  		type token1 struct {
    30  			x int 
    31  			y int
    32  		}
    33  		`,
    34  		big.NewInt(2),
    35  	},
    36  	{
    37  		"struct field from func result",
    38  		`type S struct { x int }
    39  		func fn() int { return 2 }
    40  		func F%d() int {
    41  			t := S{x: fn()}
    42  			return t.x
    43  		}
    44  		`,
    45  		big.NewInt(2),
    46  	},
    47  	{
    48  		"struct field return",
    49  		`type token2 struct {
    50  			x int
    51  			y int
    52  		}
    53  
    54  		func F%d() int {
    55  			t := token2 {
    56  				x: 2,
    57  				y: 4,
    58  			}
    59  
    60  			return t.x
    61  		}
    62  		`,
    63  		big.NewInt(2),
    64  	},
    65  	{
    66  		"struct field assign",
    67  		`type token3 struct {
    68  			x int
    69  			y int
    70  		}
    71  
    72  		func F%d() int {
    73  			t := token3 {
    74  				x: 2,
    75  				y: 4,
    76  			}
    77  			t.x = 10
    78  			return t.x
    79  		}
    80  		`,
    81  		big.NewInt(10),
    82  	},
    83  	{
    84  		"complex struct",
    85  		`type token4 struct {
    86  			x int
    87  			y int
    88  		}
    89  
    90  		func F%d() int {
    91  			x := 10
    92  
    93  			t := token4 {
    94  				x: 2,
    95  				y: 4,
    96  			}
    97  
    98  			y := x + t.x
    99  
   100  			return y
   101  		}
   102  		`,
   103  		big.NewInt(12),
   104  	},
   105  	{
   106  		"initialize struct field from variable",
   107  		`type token5 struct {
   108  			x int
   109  			y int
   110  		}
   111  
   112  		func F%d() int {
   113  			x := 10
   114  			t := token5 {
   115  				x: x,
   116  				y: 4,
   117  			}
   118  			y := t.x + t.y
   119  			return y
   120  		}
   121  		`,
   122  		big.NewInt(14),
   123  	},
   124  	{
   125  		"assign a variable to a struct field",
   126  		`type token6 struct {
   127  			x int
   128  			y int
   129  		}
   130  
   131  		func F%d() int {
   132  			ten := 10
   133  			t := token6 {
   134  				x: 2,
   135  				y: 4,
   136  			}
   137  			t.x = ten
   138  			y := t.y + t.x
   139  			return y
   140  		}
   141  		`,
   142  		big.NewInt(14),
   143  	},
   144  	{
   145  		"increase struct field with +=",
   146  		`type token7 struct { x int }
   147  		func F%d() int {
   148  		t := token7{x: 2}
   149  		t.x += 3
   150  		return t.x
   151  		}
   152  		`,
   153  		big.NewInt(5),
   154  	},
   155  	{
   156  		"assign a struct field to a struct field",
   157  		`type token8 struct {
   158  			x int
   159  			y int
   160  		}
   161  
   162  		func F%d() int {
   163  			t1 := token8 {
   164  				x: 2,
   165  				y: 4,
   166  			}
   167  			t2 := token8 {
   168  				x: 3,
   169  				y: 5,
   170  			}
   171  			t1.x = t2.y
   172  			y := t1.x + t2.x
   173  			return y
   174  		}
   175  		`,
   176  		big.NewInt(8),
   177  	},
   178  	{
   179  		"initialize same struct twice",
   180  		`type token9 struct {
   181  			x int
   182  			y int
   183  		}
   184  
   185  		func F%d() int {
   186  			t1 := token9 {
   187  				x: 2,
   188  				y: 4,
   189  			}
   190  			t2 := token9 {
   191  				x: 2,
   192  				y: 4,
   193  			}
   194  			return t1.x + t2.y
   195  		}
   196  		`,
   197  		big.NewInt(6),
   198  	},
   199  	{
   200  		"struct methods",
   201  		`type token10 struct {
   202  			x int
   203  		}
   204  
   205  		func(t token10) getInteger() int {
   206  			return t.x
   207  		}
   208  
   209  		func F%d() int {
   210  			t := token10 {
   211  				x: 4, 
   212  			}
   213  			someInt := t.getInteger()
   214  			return someInt
   215  		}
   216  		`,
   217  		big.NewInt(4),
   218  	},
   219  	{
   220  		"struct methods with arguments",
   221  		`type token11 struct {
   222  			x int
   223  		}
   224  
   225  		// Also tests if x conflicts with t.x
   226  		func(t token11) addIntegers(x int, y int) int {
   227  			return t.x + x + y
   228  		}
   229  
   230  		func F%d() int {
   231  			t := token11 {
   232  				x: 4, 
   233  			}
   234  			someInt := t.addIntegers(2, 4)
   235  			return someInt
   236  		}
   237  		`,
   238  		big.NewInt(10),
   239  	},
   240  	{
   241  		"initialize struct partially",
   242  		`type token12 struct {
   243  			x int
   244  			y int
   245  			z string
   246  			b bool
   247  		}
   248  
   249  		func F%d() int {
   250  			t := token12 {
   251  				x: 4,
   252  			}
   253  			return t.y
   254  		}
   255  		`,
   256  		big.NewInt(0),
   257  	},
   258  	{
   259  		"test return struct from func",
   260  		`type token13 struct {
   261  			x int
   262  			y int
   263  			z string
   264  			b bool
   265  		}
   266  
   267  		func newToken() token13 {
   268  			return token13{
   269  				x: 1,
   270  				y: 2, 
   271  				z: "hello",
   272  				b: false,
   273  			}
   274  		}
   275  
   276  		func F%d() token13 {
   277  			return newToken()
   278  		}
   279  		`,
   280  		[]stackitem.Item{
   281  			stackitem.NewBigInteger(big.NewInt(1)),
   282  			stackitem.NewBigInteger(big.NewInt(2)),
   283  			stackitem.NewByteArray([]byte("hello")),
   284  			stackitem.NewBool(false),
   285  		},
   286  	},
   287  	{
   288  		"pass struct as argument",
   289  		`type Bar struct {
   290  			amount int
   291  		}
   292  
   293  		func addToAmount(x int, bar Bar) int {
   294  			bar.amount = bar.amount + x
   295  			return bar.amount
   296  		}
   297  
   298  		func F%d() int {
   299  			b := Bar{
   300  				amount: 10,
   301  			}
   302  
   303  			x := addToAmount(4, b)
   304  			return x 
   305  		}
   306  		`,
   307  		big.NewInt(14),
   308  	},
   309  	{
   310  		"declare struct literal",
   311  		`func F%d() int {
   312  			var x struct {
   313  				a int
   314  			}
   315  			x.a = 2
   316  			return x.a
   317  		}
   318  		`,
   319  		big.NewInt(2),
   320  	},
   321  	{
   322  		"declare struct type",
   323  		`type withA struct {
   324  			a int
   325  		}
   326  		func F%d() int {
   327  			var x withA
   328  			x.a = 2
   329  			return x.a
   330  		}
   331  		`,
   332  		big.NewInt(2),
   333  	},
   334  	{
   335  		"nested selectors (simple read)",
   336  		`type S1 struct { x, y S2 }
   337  		type S2 struct { a, b int }
   338  		func F%d() int {
   339  			var s1 S1
   340  			var s2 S2
   341  			s2.a = 3
   342  			s1.y = s2
   343  			return s1.y.a
   344  		}
   345  		`,
   346  		big.NewInt(3),
   347  	},
   348  	{
   349  		"nested selectors (simple write)",
   350  		`type S3 struct { x S4 }
   351  		type S4 struct { a int }
   352  		func F%d() int {
   353  			s1 := S3{
   354  				x: S4 {
   355  					a: 3,
   356  				},
   357  			}
   358  			s1.x.a = 11
   359  			return s1.x.a
   360  		}
   361  		`,
   362  		big.NewInt(11),
   363  	},
   364  	{
   365  		"complex struct default value",
   366  		`type S5 struct { x S6 }
   367  		type S6 struct { y S7 }
   368  		type S7 struct { a int }
   369  		func F%d() int {
   370  			var s1 S5
   371  			s1.x.y.a = 11
   372  			return s1.x.y.a
   373  		}
   374  		`,
   375  		big.NewInt(11),
   376  	},
   377  	{
   378  		"lengthy struct default value",
   379  		`type SS struct { x int; y []byte; z bool }
   380  		func F%d() int {
   381  			var s SS
   382  			return s.x
   383  		}
   384  		`,
   385  		big.NewInt(0),
   386  	},
   387  	{
   388  		"nested selectors (complex write)",
   389  		`type S8 struct { x S9 }
   390  		type S9 struct { y, z S10 }
   391  		type S10 struct { a int }
   392  		func F%d() int {
   393  			var s1 S8
   394  			s1.x.y.a, s1.x.z.a = 11, 31
   395  			return s1.x.y.a + s1.x.z.a
   396  		}
   397  		`,
   398  		big.NewInt(42),
   399  	},
   400  	{
   401  		"omit field names",
   402  		`type pair struct { a, b int }
   403  		func F%d() int {
   404  			p := pair{1, 2}
   405  			x := p.a * 10
   406  			return x + p.b
   407  		}
   408  		`,
   409  		big.NewInt(12),
   410  	},
   411  	{
   412  		"uninitialized struct fields",
   413  		`type Foo struct {
   414                         i int
   415                         m map[string]int
   416                         b []byte
   417                         a []int
   418                         s struct { ii int }
   419                 }
   420                 func NewFoo() Foo { return Foo{} }
   421                 func F%d() int {
   422                         foo := NewFoo()
   423                         if foo.i != 0 { return 1 }
   424                         if len(foo.m) != 0 { return 1 }
   425                         if len(foo.b) != 0 { return 1 }
   426                         if len(foo.a) != 0 { return 1 }
   427                         s := foo.s
   428                         if s.ii != 0 { return 1 }
   429                         return 2
   430                 }
   431  		`,
   432  		big.NewInt(2),
   433  	},
   434  }
   435  
   436  func TestStructs(t *testing.T) {
   437  	srcBuilder := bytes.NewBuffer([]byte("package testcase\n"))
   438  	for i, tc := range structTestCases {
   439  		srcBuilder.WriteString(fmt.Sprintf(tc.src, i))
   440  	}
   441  
   442  	ne, di, err := compiler.CompileWithOptions("file.go", strings.NewReader(srcBuilder.String()), nil)
   443  	require.NoError(t, err)
   444  
   445  	for i, tc := range structTestCases {
   446  		t.Run(tc.name, func(t *testing.T) {
   447  			v := vm.New()
   448  			invokeMethod(t, fmt.Sprintf("F%d", i), ne.Script, v, di)
   449  			runAndCheck(t, v, tc.result)
   450  		})
   451  	}
   452  }