github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/compiler/for_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/smartcontract/trigger"
    12  	"github.com/nspcc-dev/neo-go/pkg/vm"
    13  	"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
    14  	"github.com/stretchr/testify/require"
    15  )
    16  
    17  func TestEntryPointWithMethod(t *testing.T) {
    18  	src := `
    19  		package foo
    20  
    21  		func Main(op string) int {
    22  			if op == "a" {
    23  				return 1
    24  			}
    25  			return 0
    26  		}
    27  	`
    28  	evalWithArgs(t, src, []byte("a"), nil, big.NewInt(1))
    29  }
    30  
    31  func TestEntryPointWithArgs(t *testing.T) {
    32  	src := `
    33  		package foo
    34  
    35  		func Main(args []any) int {
    36  			return 2 + args[1].(int)
    37  		}
    38  	`
    39  	args := []stackitem.Item{stackitem.NewBigInteger(big.NewInt(0)), stackitem.NewBigInteger(big.NewInt(1))}
    40  	evalWithArgs(t, src, nil, args, big.NewInt(3))
    41  }
    42  
    43  func TestEntryPointWithMethodAndArgs(t *testing.T) {
    44  	src := `
    45  		package foo
    46  
    47  		func Main(method string, args []any) int {
    48  			if method == "foobar" {
    49  				return 2 + args[1].(int)
    50  			}
    51  			return 0
    52  		}
    53  	`
    54  	args := []stackitem.Item{stackitem.NewBigInteger(big.NewInt(0)), stackitem.NewBigInteger(big.NewInt(1))}
    55  	evalWithArgs(t, src, []byte("foobar"), args, big.NewInt(3))
    56  }
    57  
    58  func TestArrayFieldInStruct(t *testing.T) {
    59  	src := `
    60  		package foo
    61  
    62  		type Bar struct {
    63  			arr []int
    64  		}
    65  
    66  		func Main() int {
    67  			b := Bar{
    68  				arr: []int{0, 1, 2},
    69  			}
    70  			x := b.arr[2]
    71  			return x + 2
    72  		}
    73  	`
    74  	eval(t, src, big.NewInt(4))
    75  }
    76  
    77  func TestArrayItemGetIndexBinaryExpr(t *testing.T) {
    78  	src := `
    79  		package foo
    80  		func Main() int {
    81  			x := 1
    82  			y := []int{0, 1, 2}
    83  			return y[x + 1]
    84  		}
    85  	`
    86  	eval(t, src, big.NewInt(2))
    87  }
    88  
    89  func TestArrayItemGetIndexIdent(t *testing.T) {
    90  	src := `
    91  		package foo
    92  		func Main() int {
    93  			x := 1
    94  			y := []int{0, 1, 2}
    95  			return y[x]
    96  		}
    97  	`
    98  	eval(t, src, big.NewInt(1))
    99  }
   100  
   101  func TestArrayItemBinExpr(t *testing.T) {
   102  	src := `
   103  		package foo
   104  		func Main() int {
   105  			x := []int{0, 1, 2}
   106  			return x[1] + 10
   107  		}
   108  	`
   109  	eval(t, src, big.NewInt(11))
   110  }
   111  
   112  func TestArrayItemReturn(t *testing.T) {
   113  	src := `
   114  		package foo
   115  		func Main() int {
   116  			arr := []int{0, 1, 2}
   117  			return arr[1]
   118  		}
   119  	`
   120  	eval(t, src, big.NewInt(1))
   121  }
   122  
   123  func TestArrayItemAssign(t *testing.T) {
   124  	src := `
   125  		package foo
   126  		func Main() int {
   127  			arr := []int{1, 2, 3}
   128  			y := arr[0]
   129  			return y
   130  		}
   131  	`
   132  	eval(t, src, big.NewInt(1))
   133  }
   134  
   135  func TestStringArray(t *testing.T) {
   136  	src := `
   137  		package foo
   138  		func Main() []string {
   139  			x := []string{"foo", "bar", "foobar"}
   140  			return x
   141  		}
   142  	`
   143  	eval(t, src, []stackitem.Item{
   144  		stackitem.NewByteArray([]byte("foo")),
   145  		stackitem.NewByteArray([]byte("bar")),
   146  		stackitem.NewByteArray([]byte("foobar")),
   147  	})
   148  }
   149  
   150  func TestIntArray(t *testing.T) {
   151  	src := `
   152  		package foo
   153  		func Main() []int {
   154  			arr := []int{1, 2, 3}
   155  			return arr
   156  		}
   157  	`
   158  	eval(t, src, []stackitem.Item{
   159  		stackitem.NewBigInteger(big.NewInt(1)),
   160  		stackitem.NewBigInteger(big.NewInt(2)),
   161  		stackitem.NewBigInteger(big.NewInt(3)),
   162  	})
   163  }
   164  
   165  func TestArrayLen(t *testing.T) {
   166  	src := `
   167  		package foo
   168  		func Main() int {
   169  			arr := []int{0, 1, 2}
   170  			return len(arr)
   171  		}
   172  	`
   173  	eval(t, src, big.NewInt(3))
   174  }
   175  
   176  func TestStringLen(t *testing.T) {
   177  	src := `
   178  		package foo
   179  		func Main() int {
   180  			str := "this is medium sized string"
   181  			return len(str)
   182  		}
   183  	`
   184  	eval(t, src, big.NewInt(27))
   185  }
   186  
   187  func TestByteArrayLen(t *testing.T) {
   188  	src := `
   189  		package foo
   190  
   191  		func Main() int {
   192  			b := []byte{0x00, 0x01, 0x2}
   193  			return len(b)
   194  		}
   195  	`
   196  	eval(t, src, big.NewInt(3))
   197  }
   198  
   199  func TestSimpleString(t *testing.T) {
   200  	src := `
   201  		package foo
   202  		func Main() string {
   203  			x := "NEO"
   204  			return x
   205  		}
   206  	`
   207  	eval(t, src, stackitem.NewByteArray([]byte("NEO")).Value())
   208  }
   209  
   210  func TestBoolAssign(t *testing.T) {
   211  	src := `
   212  		package foo
   213  		func Main() bool {
   214  			x := true
   215  			return x
   216  		}
   217  	`
   218  	eval(t, src, true)
   219  }
   220  
   221  func TestBoolCompare(t *testing.T) {
   222  	src := `
   223  		package foo
   224  		func Main() int {
   225  			x := true
   226  			if x {
   227  				return 10
   228  			}
   229  			return 0
   230  		}
   231  	`
   232  	eval(t, src, big.NewInt(10))
   233  }
   234  
   235  func TestBoolCompareVerbose(t *testing.T) {
   236  	src := `
   237  		package foo
   238  		func Main() int {
   239  			x := true
   240  			if x == true {
   241  				return 10
   242  			}
   243  			return 0
   244  		}
   245  	`
   246  	eval(t, src, big.NewInt(10))
   247  }
   248  
   249  func TestUnaryExpr(t *testing.T) {
   250  	src := `
   251  		package foo
   252  		func Main() bool {
   253  			x := false
   254  			return !x
   255  		}
   256  	`
   257  	eval(t, src, true)
   258  }
   259  
   260  func TestIfUnaryInvertPass(t *testing.T) {
   261  	src := `
   262  		package foo
   263  		func Main() int {
   264  			x := false
   265  			if !x { 
   266  				return 10
   267  			}
   268  			return 0
   269  		}
   270  	`
   271  	eval(t, src, big.NewInt(10))
   272  }
   273  
   274  func TestIfUnaryInvert(t *testing.T) {
   275  	src := `
   276  		package foo
   277  		func Main() int {
   278  			x := true
   279  			if !x { 
   280  				return 10
   281  			}
   282  			return 0
   283  		}
   284  	`
   285  	eval(t, src, big.NewInt(0))
   286  }
   287  
   288  func TestAppendByte(t *testing.T) {
   289  	src := `
   290  	package foo
   291  		func Main() []byte {
   292  			arr := []byte{0x00, 0x01, 0x02}
   293  			arr = append(arr, 0x03)
   294  			arr = append(arr, 0x04)
   295  			arr = append(arr, 0x05)
   296  			arr = append(arr, 0x06)
   297  			return arr
   298  		}
   299  	`
   300  	eval(t, src, []uint8{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06})
   301  }
   302  
   303  func TestAppendByteToEmpty(t *testing.T) {
   304  	src := `
   305  	package foo
   306  	func Main() []byte {
   307  		out := []byte{}
   308  		out = append(out, 1)
   309  		out = append(out, 2)
   310  		return out
   311  	}`
   312  	eval(t, src, []byte{1, 2})
   313  }
   314  
   315  func TestAppendString(t *testing.T) {
   316  	src := `
   317  	package foo
   318  		func Main() string {
   319  			arr := []string{"a", "b", "c"}
   320  			arr = append(arr, "d")
   321  			return arr[3]
   322  		}
   323  	`
   324  	eval(t, src, stackitem.NewByteArray([]byte("d")).Value())
   325  }
   326  
   327  func TestAppendInt(t *testing.T) {
   328  	src := `
   329  	package foo
   330  		func Main() int {
   331  			arr := []int{0, 1, 2}
   332  			arr = append(arr, 3)
   333  			return arr[3]
   334  		}
   335  	`
   336  	eval(t, src, big.NewInt(3))
   337  }
   338  
   339  func TestClassicForLoop(t *testing.T) {
   340  	src := `
   341  	package foo
   342  		func Main() int {
   343  			x := 0
   344  			for i := 0; i < 10; i++ {
   345  				x = i
   346  			}
   347  			return x
   348  		}
   349  	`
   350  	eval(t, src, big.NewInt(9))
   351  }
   352  
   353  func TestInc(t *testing.T) {
   354  	src := `
   355  	package foo
   356  		func Main() int {
   357  			x := 0
   358  			x++
   359  			return x
   360  		}
   361  	`
   362  
   363  	eval(t, src, big.NewInt(1))
   364  }
   365  
   366  func TestDec(t *testing.T) {
   367  	src := `
   368  	package foo
   369  		func Main() int {
   370  			x := 2
   371  			x--
   372  			return x
   373  		}
   374  	`
   375  
   376  	eval(t, src, big.NewInt(1))
   377  }
   378  
   379  var forLoopTestCases = []testCase{
   380  	{
   381  		"empty for loop",
   382  		`func F%d() int {
   383  			x := 0
   384  			for {
   385  				x++
   386  				if x == 2 { break }
   387  			}
   388  			return x
   389  		}
   390  		`,
   391  		big.NewInt(2),
   392  	}, {
   393  		"big iteration count",
   394  		`func F%d() int {
   395  			x := 0
   396  			for i := 0; i < 100000; i++ {
   397  				x = i
   398  			}
   399  			return x
   400  		}
   401  		`,
   402  		big.NewInt(99999),
   403  	},
   404  	{
   405  		"no init",
   406  		`func F%d() int {
   407  			i := 0
   408  			for ; i < 10; i++ {
   409  			}
   410  			return i
   411  		}
   412  	`,
   413  		big.NewInt(10),
   414  	},
   415  	{
   416  		"no post",
   417  		`func F%d() int {
   418  			i := 0
   419  			for i < 10 {
   420  				i++
   421  			}
   422  			return i
   423  		}
   424  		`,
   425  		big.NewInt(10),
   426  	},
   427  	{
   428  		"range",
   429  		`func F%d() int {
   430  			sum := 0
   431  			arr := []int{1, 2, 3}
   432  			for i := range arr {
   433  				sum += arr[i] 
   434  			}
   435  			return sum
   436  		}
   437  		`,
   438  		big.NewInt(6),
   439  	},
   440  	{
   441  		"range, global index",
   442  		`func F%d() int {
   443  			sum := 0
   444  			i := 0
   445  			arr := []int{1, 2, 3}
   446  			for i = range arr {
   447  				sum += arr[i]
   448  			}
   449  			return sum + i
   450  		}
   451  		`,
   452  		big.NewInt(8),
   453  	},
   454  	{
   455  		"range, change variable",
   456  		`func F%d() int {
   457  			sum := 0
   458  			arr := []int{1, 2, 3}
   459  			for i := range arr {
   460  				sum += arr[i]
   461  				i++
   462  				sum += i
   463  			}
   464  			return sum
   465  		}
   466  		`,
   467  		big.NewInt(12),
   468  	},
   469  	{
   470  		"break",
   471  		`func F%d() int {
   472  			var i int
   473  			for i < 10 {
   474  				i++
   475  				if i == 5 {
   476  					break
   477  				}
   478  			}
   479  			return i
   480  		}
   481  		`,
   482  		big.NewInt(5),
   483  	},
   484  	{
   485  		"break label",
   486  		`func F%d() int {
   487  			var i int
   488  			loop:
   489  			for i < 10 {
   490  				i++
   491  				if i == 5 {
   492  					break loop
   493  				}
   494  			}
   495  			return i
   496  		}
   497  		`,
   498  		big.NewInt(5),
   499  	},
   500  	{
   501  		"nested break",
   502  		`func F%d() int {
   503  			var i int
   504  			for i < 10 {
   505  				i++
   506  				for j := 0; j < 2; j++ {
   507  					i++
   508  					if i == 5 {
   509  						break
   510  					}
   511  				}
   512  			}
   513  			return i
   514  		}
   515  		`,
   516  		big.NewInt(11),
   517  	},
   518  	{
   519  		"nested break label",
   520  		`func F%d() int {
   521  			var i int
   522  			loop:
   523  			for i < 10 {
   524  				i++
   525  				for j := 0; j < 2; j++ {
   526  					if i == 5 {
   527  						break loop
   528  					}
   529  					i++
   530  				}
   531  			}
   532  			return i
   533  		}
   534  		`,
   535  		big.NewInt(5),
   536  	},
   537  	{
   538  		"continue",
   539  		`func F%d() int {
   540  			var i, j int
   541  			for i < 10 {
   542  				i++
   543  				if i >= 5 {
   544  					continue
   545  				}
   546  				j++
   547  			}
   548  			return j
   549  		}
   550  		`,
   551  		big.NewInt(4),
   552  	},
   553  	{
   554  		"continue label",
   555  		`func F%d() int {
   556  			var i, j int
   557  			loop:
   558  			for i < 10 {
   559  				i++
   560  				if i >= 5 {
   561  					continue loop
   562  				}
   563  				j++
   564  			}
   565  			return j
   566  		}
   567  		`,
   568  		big.NewInt(4),
   569  	},
   570  	{
   571  		"nested continue",
   572  		`func F%d() int {
   573  			var i, k int
   574  			for i < 10 {
   575  				i++
   576  				for j := 0; j < 3; j++ {
   577  					if j >= 2 {
   578  						continue
   579  					}
   580  					k++
   581  				}
   582  			}
   583  			return k
   584  		}
   585  		`,
   586  		big.NewInt(20),
   587  	},
   588  	{
   589  		"nested continue label",
   590  		`func F%d() int {
   591  			var i int
   592  			loop:
   593  			for ; i < 10; i += 10 {
   594  				i++
   595  				for j := 0; j < 4; j++ {
   596  					if i == 5 {
   597  						continue loop
   598  					}
   599  					i++
   600  				}
   601  			}
   602  			return i
   603  		}
   604  		`,
   605  		big.NewInt(15),
   606  	},
   607  	{
   608  		"range break",
   609  		`func F%d() int {
   610  			var i int
   611  			arr := []int{1, 2, 3}
   612  			for i = range arr {
   613  				if arr[i] == 2 {
   614  					break
   615  				}
   616  			}
   617  			return i
   618  		}
   619  		`,
   620  		big.NewInt(1),
   621  	},
   622  	{
   623  		"range nested break",
   624  		`func F%d() int {
   625  			k := 5
   626  			arr := []int{1, 2, 3}
   627  			urr := []int{4, 5, 6, 7}
   628  			loop:
   629  			for range arr {
   630  				k++
   631  				for j := range urr {
   632  					k++
   633  					if j == 3 {
   634  						break loop
   635  					}
   636  				}
   637  			}
   638  			return k
   639  		}
   640  		`,
   641  		big.NewInt(10),
   642  	},
   643  	{
   644  		"range continue",
   645  		`func F%d() int {
   646  			i := 6
   647  			arr := []int{1, 2, 3}
   648  			for j := range arr {
   649  				if arr[j] < 2 {
   650  					continue
   651  				}
   652  				i++
   653  			}
   654  			return i
   655  		}
   656  		`,
   657  		big.NewInt(8),
   658  	},
   659  	{
   660  		"range, no variable",
   661  		`func F%d() int {
   662  			sum := 0
   663  			arr := []int{1, 2, 3}
   664  			for range arr {
   665  				sum += 1
   666  			}
   667  			return sum
   668  		}
   669  		`,
   670  		big.NewInt(3),
   671  	},
   672  	{
   673  		"range value",
   674  		`func f(a int) int { return a }
   675  		func F%d() int {
   676  			var sum int
   677  			arr := []int{1, 9, 4}
   678  			for _, v := range arr {
   679  				sum += f(v)
   680  			}
   681  			return sum
   682  		}
   683  		`,
   684  		big.NewInt(14),
   685  	},
   686  	{
   687  		"range, map",
   688  		`func F%d() int {
   689  			m := map[int]int{
   690  				1: 13,
   691  				11: 17,
   692  			}
   693  			var sum int
   694  			for i, v := range m {
   695  				sum += i
   696  				sum += v
   697  			}
   698  			return sum
   699  		}
   700  		`,
   701  		big.NewInt(42),
   702  	},
   703  	{
   704  		"range, type conversion",
   705  		`type intArr []int
   706  		func F%d() int {
   707  			a := []int{1, 2, 3}
   708  			s := 0
   709  			for _, v := range intArr(a) {
   710  				s += v
   711  			}
   712  			return s
   713  		}
   714  		`,
   715  		big.NewInt(6),
   716  	},
   717  	{
   718  		"shadow range key",
   719  		`func F%d() int {
   720  			i := 10
   721  			ints := []int{1, 2, 3, 4, 5}
   722  			for i := range ints {
   723  				_ = i
   724  			}
   725  			return i
   726  		}
   727  		`,
   728  		big.NewInt(10),
   729  	},
   730  	{
   731  		"shadow range value",
   732  		`func F%d() int {
   733  			i := 10
   734  			ints := []int{1, 2, 3, 4, 5}
   735  			for _, i := range ints {
   736  				_ = i
   737  			}
   738  			return i
   739  		}
   740  		`,
   741  		big.NewInt(10),
   742  	},
   743  }
   744  
   745  func TestForLoop(t *testing.T) {
   746  	srcBuilder := bytes.NewBuffer([]byte("package testcase\n"))
   747  	for i, tc := range forLoopTestCases {
   748  		srcBuilder.WriteString(fmt.Sprintf(tc.src, i))
   749  	}
   750  
   751  	ne, di, err := compiler.CompileWithOptions("file.go", strings.NewReader(srcBuilder.String()), nil)
   752  	require.NoError(t, err)
   753  
   754  	for i, tc := range forLoopTestCases {
   755  		v := vm.New()
   756  		t.Run(tc.name, func(t *testing.T) {
   757  			v.Reset(trigger.Application)
   758  			invokeMethod(t, fmt.Sprintf("F%d", i), ne.Script, v, di)
   759  			runAndCheck(t, v, tc.result)
   760  		})
   761  	}
   762  }
   763  
   764  func TestForLoopComplexConditions(t *testing.T) {
   765  	forCondTestCases := []struct {
   766  		Name   string
   767  		Cond   string
   768  		Assign string
   769  		Result int64
   770  	}{
   771  		{Cond: "i < 3 && j < 2", Result: 2},
   772  		{Cond: "i < 3 || j < 2", Result: 3},
   773  		{Cond: "i < 3 && (j < 2 || i < 1)", Result: 2},
   774  		{Cond: "i < 3 && (j < 2 && i < 1)", Result: 1},
   775  		{Cond: "(i < 1 || j < 3) && (i < 3 || j < 1)", Result: 3},
   776  		{Cond: "(i < 2 && j < 4) || (i < 4 && j < 2)", Result: 2},
   777  		{Cond: "ok", Assign: "ok = i < 3 && j < 2", Result: 2},
   778  		{Cond: "ok", Assign: "ok = i < 3 || j < 2", Result: 3},
   779  		{Cond: "ok", Assign: "ok = i < 3 && (j < 2 || i < 1)", Result: 2},
   780  		{Cond: "ok", Assign: "ok = i < 3 && (j < 2 && i < 1)", Result: 1},
   781  		{Cond: "ok", Assign: "ok = (i < 1 || j < 3) && (i < 3 || j < 1)", Result: 3},
   782  		{Cond: "ok", Assign: "ok = (i < 2 && j < 4) || (i < 4 && j < 2)", Result: 2},
   783  	}
   784  
   785  	tmpl := `func F%d() int {
   786  		var ok bool
   787  		_ = ok
   788  		i := 0
   789  		j := 0
   790  		%s
   791  		for %s {
   792  			i++
   793  			j++
   794  			%s
   795  		}
   796  		return i
   797  	}
   798  	`
   799  	srcBuilder := bytes.NewBufferString("package foo\n")
   800  	for i, tc := range forCondTestCases {
   801  		srcBuilder.WriteString(fmt.Sprintf(tmpl, i, tc.Assign, tc.Cond, tc.Assign))
   802  	}
   803  
   804  	ne, di, err := compiler.CompileWithOptions("file.go", strings.NewReader(srcBuilder.String()), nil)
   805  	require.NoError(t, err)
   806  
   807  	for i, tc := range forCondTestCases {
   808  		v := vm.New()
   809  		name := tc.Cond
   810  		if tc.Assign != "" {
   811  			name = tc.Assign
   812  		}
   813  		t.Run(name, func(t *testing.T) {
   814  			v.Reset(trigger.Application)
   815  			invokeMethod(t, fmt.Sprintf("F%d", i), ne.Script, v, di)
   816  			runAndCheck(t, v, big.NewInt(tc.Result))
   817  		})
   818  	}
   819  }