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 }