github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/compiler/slice_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 sliceTestCases = []testCase{
    17  	{
    18  		"constant index",
    19  		`func F%d() int {
    20  			a := []int{0,0}
    21  			a[1] = 42
    22  			return a[1]+0
    23  		}
    24  		`,
    25  		big.NewInt(42),
    26  	},
    27  	{
    28  		"variable index",
    29  		`func F%d() int {
    30  			a := []int{0,0}
    31  			i := 1
    32  			a[i] = 42
    33  			return a[1]+0
    34  		}
    35  		`,
    36  		big.NewInt(42),
    37  	},
    38  	{
    39  		"increase slice element with +=",
    40  		`func F%d() int {
    41  			a := []int{1, 2, 3}
    42  			a[1] += 40
    43  			return a[1]
    44  		}
    45  		`,
    46  		big.NewInt(42),
    47  	},
    48  	{
    49  		"complex test",
    50  		`func F%d() int {
    51  			a := []int{1,2,3}
    52  			x := a[0]
    53  			a[x] = a[x] + 4
    54  			a[x] = a[x] + a[2]
    55  			return a[1]
    56  		}
    57  		`,
    58  		big.NewInt(9),
    59  	},
    60  	{
    61  		"slice literals with variables",
    62  		`func F%d() int {
    63  			elem := 7
    64  			a := []int{6, elem, 8}
    65  			return a[1]
    66  		}
    67  		`,
    68  		big.NewInt(7),
    69  	},
    70  	{
    71  		"slice literals with expressions",
    72  		`func F%d() int {
    73  			elem := []int{3, 7}
    74  			a := []int{6, elem[1]*2+1, 24}
    75  			return a[1]
    76  		}
    77  		`,
    78  		big.NewInt(15),
    79  	},
    80  	{
    81  		"sub-slice with literal bounds",
    82  		`func F%d() []byte {
    83  			a := []byte{0, 1, 2, 3}
    84  			b := a[1:3]
    85  			return b
    86  		}
    87  		`,
    88  		[]byte{1, 2},
    89  	},
    90  	{
    91  		"sub-slice with constant bounds",
    92  		`const x = 1
    93  		const y = 3
    94  		func F%d() []byte {
    95  			a := []byte{0, 1, 2, 3}
    96  			b := a[x:y]
    97  			return b
    98  		}
    99  		`,
   100  		[]byte{1, 2},
   101  	},
   102  	{
   103  		"sub-slice with variable bounds",
   104  		`func F%d() []byte {
   105  			a := []byte{0, 1, 2, 3}
   106  			x := 1
   107  			y := 3
   108  			b := a[x:y]
   109  			return b
   110  		}
   111  		`,
   112  		[]byte{1, 2},
   113  	},
   114  	{
   115  		"sub-slice with no lower bound",
   116  		`func F%d() []byte {
   117  			a := []byte{0, 1, 2, 3}
   118  			b := a[:3]
   119  			return b
   120  		}
   121  		`,
   122  		[]byte{0, 1, 2},
   123  	},
   124  	{
   125  		"sub-slice with no upper bound",
   126  		`func F%d() []byte {
   127  			a := []byte{0, 1, 2, 3}
   128  			b := a[2:]
   129  			return b
   130  		}
   131  		`,
   132  		[]byte{2, 3},
   133  	},
   134  	{
   135  		"declare byte slice",
   136  		`func F%d() []byte {
   137  			var a []byte
   138  			a = append(a, 1)
   139  			a = append(a, 2)
   140  			return a
   141  		}
   142  		`,
   143  		[]byte{1, 2},
   144  	},
   145  	{
   146  		"append multiple bytes to a slice",
   147  		`func F%d() []byte {
   148  			var a []byte
   149  			a = append(a, 1, 2)
   150  			return a
   151  		}
   152  		`,
   153  		[]byte{1, 2},
   154  	},
   155  	{
   156  		"append multiple ints to a slice",
   157  		`func F%d() []int {
   158  			var a []int
   159  			a = append(a, 1, 2, 3)
   160  			a = append(a, 4, 5)
   161  			return a
   162  		}
   163  		`,
   164  		[]stackitem.Item{
   165  			stackitem.NewBigInteger(big.NewInt(1)),
   166  			stackitem.NewBigInteger(big.NewInt(2)),
   167  			stackitem.NewBigInteger(big.NewInt(3)),
   168  			stackitem.NewBigInteger(big.NewInt(4)),
   169  			stackitem.NewBigInteger(big.NewInt(5)),
   170  		},
   171  	},
   172  	{
   173  		"int slice, append slice",
   174  		`	func getByte() byte { return 0x80 }
   175  			func F%d() []int {
   176  				x := []int{1}
   177  				y := []int{2, 3}
   178  				x = append(x, y...)
   179  				x = append(x, y...)
   180  				return x
   181  			}
   182  		`,
   183  		[]stackitem.Item{
   184  			stackitem.Make(1),
   185  			stackitem.Make(2), stackitem.Make(3),
   186  			stackitem.Make(2), stackitem.Make(3),
   187  		},
   188  	},
   189  	{
   190  		"declare compound slice",
   191  		`func F%d() []string {
   192  			var a []string
   193  			a = append(a, "a")
   194  			a = append(a, "b")
   195  			return a
   196  		}
   197  		`,
   198  		[]stackitem.Item{
   199  			stackitem.NewByteArray([]byte("a")),
   200  			stackitem.NewByteArray([]byte("b")),
   201  		},
   202  	},
   203  	{
   204  		"declare compound slice alias",
   205  		`type strs []string
   206  		func F%d() []string {
   207  			var a strs
   208  			a = append(a, "a")
   209  			a = append(a, "b")
   210  			return a
   211  		}
   212  		`,
   213  		[]stackitem.Item{
   214  			stackitem.NewByteArray([]byte("a")),
   215  			stackitem.NewByteArray([]byte("b")),
   216  		},
   217  	},
   218  	{
   219  		"byte-slice assignment",
   220  		`func F%d() []byte {
   221  			a := []byte{0, 1, 2}
   222  			a[1] = 42
   223  			return a
   224  		}
   225  		`,
   226  		[]byte{0, 42, 2},
   227  	},
   228  	{
   229  		"byte-slice assignment after string conversion",
   230  		`func F%d() []byte {
   231  			a := "abc"
   232  			b := []byte(a)
   233  			b[1] = 42
   234  			return []byte(a)
   235  		}
   236  		`,
   237  		[]byte{0x61, 0x62, 0x63},
   238  	},
   239  	{
   240  		"declare and append byte-slice",
   241  		`func F%d() []byte {
   242  			var a []byte
   243  			a = append(a, 1)
   244  			a = append(a, 2)
   245  			return a
   246  		}
   247  		`,
   248  		[]byte{1, 2},
   249  	},
   250  	{
   251  		"nested slice assignment",
   252  		`func F%d() int {
   253  			a := [][]int{[]int{1, 2}, []int{3, 4}}
   254  			a[1][0] = 42
   255  			return a[1][0]
   256  		}
   257  		`,
   258  		big.NewInt(42),
   259  	},
   260  	{
   261  		"nested slice omitted type (slice)",
   262  		`func F%d() int {
   263  			a := [][]int{{1, 2}, {3, 4}}
   264  			a[1][0] = 42
   265  			return a[1][0]
   266  		}
   267  		`,
   268  		big.NewInt(42),
   269  	},
   270  	{
   271  		"nested slice omitted type (struct)",
   272  		`type pairA struct { a, b int }
   273  		func F%d() int {
   274  			a := []pairA{{a: 1, b: 2}, {a: 3, b: 4}}
   275  			a[1].a = 42
   276  			return a[1].a
   277  		}
   278  		`,
   279  		big.NewInt(42),
   280  	},
   281  	{
   282  		"defaults to nil for byte slice",
   283  		`func F%d() int {
   284  			var a []byte
   285  			if a != nil { return 1}
   286  			return 2
   287  		}
   288  		`,
   289  		big.NewInt(2),
   290  	},
   291  	{
   292  		"defaults to nil for int slice",
   293  		`func F%d() int {
   294  			var a []int
   295  			if a != nil { return 1}
   296  			return 2
   297  		}
   298  		`,
   299  		big.NewInt(2),
   300  	},
   301  	{
   302  		"defaults to nil for struct slice",
   303  		`type pairB struct { a, b int }
   304  		func F%d() int {
   305  			var a []pairB
   306  			if a != nil { return 1}
   307  			return 2
   308  		}
   309  		`,
   310  		big.NewInt(2),
   311  	},
   312  	{
   313  		"literal byte-slice with variable values",
   314  		`const sym1 = 's'
   315  		func F%d() []byte {
   316  			sym2 := byte('t')
   317  			sym4 := byte('i')
   318  			return []byte{sym1, sym2, 'r', sym4, 'n', 'g'}
   319  		}
   320  		`,
   321  		[]byte("string"),
   322  	},
   323  	{
   324  		"literal slice with function call",
   325  		`func fn() byte { return 't' }
   326  		func F%d() []byte {
   327  			return []byte{'s', fn(), 'r'}
   328  		}
   329  		`,
   330  		[]byte("str"),
   331  	},
   332  }
   333  
   334  func TestSliceOperations(t *testing.T) {
   335  	srcBuilder := bytes.NewBuffer([]byte("package testcase\n"))
   336  	for i, tc := range sliceTestCases {
   337  		srcBuilder.WriteString(fmt.Sprintf(tc.src, i))
   338  	}
   339  
   340  	ne, di, err := compiler.CompileWithOptions("file.go", strings.NewReader(srcBuilder.String()), nil)
   341  	require.NoError(t, err)
   342  
   343  	for i, tc := range sliceTestCases {
   344  		t.Run(tc.name, func(t *testing.T) {
   345  			v := vm.New()
   346  			invokeMethod(t, fmt.Sprintf("F%d", i), ne.Script, v, di)
   347  			runAndCheck(t, v, tc.result)
   348  		})
   349  	}
   350  }
   351  
   352  func TestByteSlices(t *testing.T) {
   353  	testCases := []testCase{
   354  		{
   355  			"append bytes bigger than 0x79",
   356  			`package foo
   357  			func Main() []byte {
   358  				var z []byte
   359  				z = append(z, 0x78, 0x79, 0x80, 0x81, 0xFF)
   360  				return z
   361  			}`,
   362  			[]byte{0x78, 0x79, 0x80, 0x81, 0xFF},
   363  		},
   364  		{
   365  			"append bytes bigger than 0x79, not nil",
   366  			`package foo
   367  			func Main() []byte {
   368  				z := []byte{0x78}
   369  				z = append(z, 0x79, 0x80, 0x81, 0xFF)
   370  				return z
   371  			}`,
   372  			[]byte{0x78, 0x79, 0x80, 0x81, 0xFF},
   373  		},
   374  		{
   375  			"append bytes bigger than 0x79, function return",
   376  			`package foo
   377  			func getByte() byte { return 0x80 }
   378  			func Main() []byte {
   379  				var z []byte
   380  				z = append(z, 0x78, 0x79, getByte(), 0x81, 0xFF)
   381  				return z
   382  			}`,
   383  			[]byte{0x78, 0x79, 0x80, 0x81, 0xFF},
   384  		},
   385  		{
   386  			"append ints bigger than 0x79, function return byte",
   387  			`package foo
   388  			func getByte() byte { return 0x80 }
   389  			func Main() []int {
   390  				var z []int
   391  				z = append(z, 0x78, int(getByte()))
   392  				return z
   393  			}`,
   394  			[]stackitem.Item{stackitem.Make(0x78), stackitem.Make(0x80)},
   395  		},
   396  		{
   397  			"slice literal, bytes bigger than 0x79, function return",
   398  			`package foo
   399  			func getByte() byte { return 0x80 }
   400  			func Main() []byte {
   401  				z := []byte{0x79, getByte(), 0x81}
   402  				return z
   403  			}`,
   404  			[]byte{0x79, 0x80, 0x81},
   405  		},
   406  		{
   407  			"compare bytes as integers",
   408  			`package foo
   409  				func getByte1() byte { return 0x79 }
   410  				func getByte2() byte { return 0x80 }
   411  				func Main() bool {
   412  					return getByte1() < getByte2()
   413  				}`,
   414  			true,
   415  		},
   416  	}
   417  	runTestCases(t, testCases)
   418  }
   419  
   420  func TestSubsliceCompound(t *testing.T) {
   421  	src := `package foo
   422  	func Main() []int {
   423  		a := []int{0, 1, 2, 3}
   424  		b := a[1:3]
   425  		return b
   426  	}`
   427  	_, err := compiler.Compile("", strings.NewReader(src))
   428  	require.Error(t, err)
   429  }
   430  
   431  func TestSubsliceFromStructField(t *testing.T) {
   432  	src := `package foo
   433  	type pair struct { key, value []byte }
   434  	func Main() []byte {
   435  		p := pair{ []byte{1}, []byte{4, 8, 15, 16, 23, 42} }
   436  		return p.value[2:4]
   437  	}`
   438  	eval(t, src, []byte{15, 16})
   439  }
   440  
   441  func TestRemove(t *testing.T) {
   442  	t.Run("Valid", func(t *testing.T) {
   443  		src := `package foo
   444  		import "github.com/nspcc-dev/neo-go/pkg/interop/util"
   445  		func Main() int {
   446  			a := []int{11, 22, 33}
   447  			util.Remove(a, 1)
   448  			return len(a) + a[0] + a[1]
   449  		}`
   450  		eval(t, src, big.NewInt(46))
   451  	})
   452  	t.Run("ByteSlice", func(t *testing.T) {
   453  		// This test checks that `Remove` has correct arguments.
   454  		// After `Remove` became an opcode it is harder to do such checks.
   455  		// Skip the test for now.
   456  		t.Skip()
   457  		src := `package foo
   458  		import "github.com/nspcc-dev/neo-go/pkg/interop/util"
   459  		func Main() int {
   460  			a := []byte{11, 22, 33}
   461  			util.Remove(a, 1)
   462  			return len(a)
   463  		}`
   464  		_, err := compiler.Compile("", strings.NewReader(src))
   465  		require.Error(t, err)
   466  	})
   467  }
   468  
   469  func TestJumps(t *testing.T) {
   470  	src := `
   471  	package foo
   472  	func Main() []byte {
   473  		buf := []byte{0x62, 0x01, 0x00}
   474  		return buf
   475  	}
   476  	`
   477  	eval(t, src, []byte{0x62, 0x01, 0x00})
   478  }
   479  
   480  func TestMake(t *testing.T) {
   481  	t.Run("Map", func(t *testing.T) {
   482  		src := `package foo
   483  		func Main() int {
   484  			a := make(map[int]int)
   485  			a[1] = 10
   486  			a[2] = 20
   487  			return a[1]
   488  		}`
   489  		eval(t, src, big.NewInt(10))
   490  	})
   491  	t.Run("IntSlice", func(t *testing.T) {
   492  		src := `package foo
   493  		func Main() int {
   494  			a := make([]int, 10)
   495  			return len(a) + a[0]
   496  		}`
   497  		eval(t, src, big.NewInt(10))
   498  	})
   499  	t.Run("ByteSlice", func(t *testing.T) {
   500  		src := `package foo
   501  		func Main() int {
   502  			a := make([]byte, 10)
   503  			return len(a) + int(a[0])
   504  		}`
   505  		eval(t, src, big.NewInt(10))
   506  	})
   507  	t.Run("CapacityError", func(t *testing.T) {
   508  		src := `package foo
   509  		func Main() int {
   510  			a := make([]int, 1, 2)
   511  			return a[0]
   512  		}`
   513  		_, err := compiler.Compile("foo.go", strings.NewReader(src))
   514  		require.Error(t, err)
   515  	})
   516  }
   517  
   518  func TestCopy(t *testing.T) {
   519  	t.Run("Invalid", func(t *testing.T) {
   520  		src := `package foo
   521  		func Main() []int {
   522  			src := []int{3, 2, 1}
   523  			dst := make([]int, 2)
   524  			copy(dst, src)
   525  			return dst
   526  		}`
   527  		_, err := compiler.Compile("foo.go", strings.NewReader(src))
   528  		require.Error(t, err)
   529  	})
   530  	t.Run("Simple", func(t *testing.T) {
   531  		src := `package foo
   532  		func Main() []byte {
   533  			src := []byte{3, 2, 1}
   534  			dst := make([]byte, 2)
   535  			copy(dst, src)
   536  			return dst
   537  		}`
   538  		eval(t, src, []byte{3, 2})
   539  	})
   540  	t.Run("LowSrcIndex", func(t *testing.T) {
   541  		src := `package foo
   542  		func Main() []byte {
   543  			src := []byte{3, 2, 1}
   544  			dst := make([]byte, 2)
   545  			copy(dst, src[1:])
   546  			return dst
   547  		}`
   548  		eval(t, src, []byte{2, 1})
   549  	})
   550  	t.Run("LowDstIndex", func(t *testing.T) {
   551  		src := `package foo
   552  		func Main() []byte {
   553  			src := []byte{3, 2, 1}
   554  			dst := make([]byte, 2)
   555  			copy(dst[1:], src[1:])
   556  			return dst
   557  		}`
   558  		eval(t, src, []byte{0, 2})
   559  	})
   560  	t.Run("BothIndices", func(t *testing.T) {
   561  		src := `package foo
   562  		func Main() []byte {
   563  			src := []byte{4, 3, 2, 1}
   564  			dst := make([]byte, 4)
   565  			copy(dst[1:], src[1:3])
   566  			return dst
   567  		}`
   568  		eval(t, src, []byte{0, 3, 2, 0})
   569  	})
   570  	t.Run("EmptySliceExpr", func(t *testing.T) {
   571  		src := `package foo
   572  		func Main() []byte {
   573  			src := []byte{3, 2, 1}
   574  			dst := make([]byte, 2)
   575  			copy(dst[1:], src[:])
   576  			return dst
   577  		}`
   578  		eval(t, src, []byte{0, 3})
   579  	})
   580  	t.Run("AssignToVariable", func(t *testing.T) {
   581  		src := `package foo
   582  		func Main() int {
   583  			src := []byte{3, 2, 1}
   584  			dst := make([]byte, 2)
   585  			n := copy(dst, src)
   586  			return n
   587  		}`
   588  		eval(t, src, big.NewInt(2))
   589  	})
   590  }