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 }