github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/compiler/global_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/opcode" 13 "github.com/stretchr/testify/require" 14 ) 15 16 func TestUnusedGlobal(t *testing.T) { 17 t.Run("simple unused", func(t *testing.T) { 18 src := `package foo 19 const ( 20 _ int = iota 21 a 22 ) 23 func Main() int { 24 return 1 25 }` 26 prog := eval(t, src, big.NewInt(1)) 27 require.Equal(t, 2, len(prog)) // PUSH1 + RET 28 }) 29 t.Run("unused with function call inside", func(t *testing.T) { 30 t.Run("specification names count matches values count", func(t *testing.T) { 31 src := `package foo 32 var control int 33 var _ = f() 34 func Main() int { 35 return control 36 } 37 func f() int { 38 control = 1 39 return 5 40 }` 41 eval(t, src, big.NewInt(1)) 42 }) 43 t.Run("specification names count differs from values count", func(t *testing.T) { 44 src := `package foo 45 var control int 46 var _, _ = f() 47 func Main() int { 48 return control 49 } 50 func f() (int, int) { 51 control = 1 52 return 5, 6 53 }` 54 eval(t, src, big.NewInt(1)) 55 }) 56 t.Run("used", func(t *testing.T) { 57 src := `package foo 58 var _, A = f() 59 func Main() int { 60 return A 61 } 62 func f() (int, int) { 63 return 5, 6 64 }` 65 eval(t, src, big.NewInt(6)) 66 checkInstrCount(t, src, 1, 1, 0, 0) // sslot for A, single call to f 67 }) 68 }) 69 t.Run("unused without function call", func(t *testing.T) { 70 src := `package foo 71 var _ = 1 72 var ( 73 _ = 2 + 3 74 _, _ = 3 + 4, 5 75 ) 76 func Main() int { 77 return 1 78 }` 79 prog := eval(t, src, big.NewInt(1)) 80 require.Equal(t, 2, len(prog)) // PUSH1 + RET 81 }) 82 } 83 84 func TestUnusedOptimizedGlobalVar(t *testing.T) { 85 t.Run("unused, no initialization", func(t *testing.T) { 86 src := `package foo 87 var A int 88 var ( 89 B int 90 C, D, E int 91 ) 92 func Main() int { 93 return 1 94 }` 95 prog := eval(t, src, big.NewInt(1)) 96 require.Equal(t, 2, len(prog)) // Main 97 }) 98 t.Run("used, no initialization", func(t *testing.T) { 99 src := `package foo 100 var A int 101 func Main() int { 102 return A 103 }` 104 eval(t, src, big.NewInt(0)) 105 checkInstrCount(t, src, 1, 0, 0, 0) // sslot for A 106 }) 107 t.Run("used by unused var, no initialization", func(t *testing.T) { 108 src := `package foo 109 var Unused int 110 var Unused2 = Unused + 1 111 func Main() int { 112 return 1 113 }` 114 prog := eval(t, src, big.NewInt(1)) 115 require.Equal(t, 2, len(prog)) // Main 116 }) 117 t.Run("unused, with initialization", func(t *testing.T) { 118 src := `package foo 119 var Unused = 1 120 func Main() int { 121 return 2 122 }` 123 prog := eval(t, src, big.NewInt(2)) 124 require.Equal(t, 2, len(prog)) // Main 125 }) 126 t.Run("unused, with initialization by used var", func(t *testing.T) { 127 src := `package foo 128 var ( 129 A = 1 130 B, Unused, C = f(), A + 2, 3 // the code for Unused initialization won't be emitted as it's a pure expression without function calls 131 Unused2 = 4 132 ) 133 var Unused3 = 5 134 func Main() int { 135 return A + C 136 } 137 func f() int { 138 return 4 139 }` 140 eval(t, src, big.NewInt(4), []any{opcode.INITSSLOT, []byte{2}}, // sslot for A and C 141 opcode.PUSH1, opcode.STSFLD0, // store A 142 []any{opcode.CALL, []byte{10}}, opcode.DROP, // evaluate B and drop 143 opcode.PUSH3, opcode.STSFLD1, opcode.RET, // store C 144 opcode.LDSFLD0, opcode.LDSFLD1, opcode.ADD, opcode.RET, // Main 145 opcode.PUSH4, opcode.RET) // f 146 }) 147 t.Run("used by unused var, with initialization", func(t *testing.T) { 148 src := `package foo 149 var ( 150 Unused1 = 1 151 Unused2 = Unused1 + 1 152 ) 153 func Main() int { 154 return 1 155 }` 156 prog := eval(t, src, big.NewInt(1)) 157 require.Equal(t, 2, len(prog)) // Main 158 }) 159 t.Run("used with combination of nested unused", func(t *testing.T) { 160 src := `package foo 161 var ( 162 A = 1 163 Unused1 = 2 164 Unused2 = Unused1 + 1 165 ) 166 func Main() int { 167 return A 168 }` 169 eval(t, src, big.NewInt(1), []any{opcode.INITSSLOT, []byte{1}}, // sslot for A 170 opcode.PUSH1, opcode.STSFLD0, opcode.RET, // store A 171 opcode.LDSFLD0, opcode.RET) // Main 172 }) 173 t.Run("single var stmt with both used and unused vars", func(t *testing.T) { 174 src := `package foo 175 var A, Unused1, B, Unused2 = 1, 2, 3, 4 176 func Main() int { 177 return A + B 178 }` 179 eval(t, src, big.NewInt(4), []any{opcode.INITSSLOT, []byte{2}}, // sslot for A and B 180 opcode.PUSH1, opcode.STSFLD0, // store A 181 opcode.PUSH3, opcode.STSFLD1, opcode.RET, // store B 182 opcode.LDSFLD0, opcode.LDSFLD1, opcode.ADD, opcode.RET) // Main 183 }) 184 t.Run("single var decl token with multiple var specifications", func(t *testing.T) { 185 src := `package foo 186 var ( 187 A, Unused1, B, Unused2 = 1, 2, 3, 4 188 C, Unused3 int 189 ) 190 func Main() int { 191 return A + B + C 192 }` 193 eval(t, src, big.NewInt(4), []any{opcode.INITSSLOT, []byte{3}}, // sslot for A, B, C 194 opcode.PUSH1, opcode.STSFLD0, // store A 195 opcode.PUSH3, opcode.STSFLD1, // store B 196 opcode.PUSH0, opcode.STSFLD2, opcode.RET, // store C 197 opcode.LDSFLD0, opcode.LDSFLD1, opcode.ADD, opcode.LDSFLD2, opcode.ADD, opcode.RET) // Main 198 }) 199 t.Run("function as unused var value", func(t *testing.T) { 200 src := `package foo 201 var A, Unused1 = 1, f() 202 func Main() int { 203 return A 204 } 205 func f() int { 206 return 2 207 }` 208 eval(t, src, big.NewInt(1), []any{opcode.INITSSLOT, []byte{1}}, // sslot for A 209 opcode.PUSH1, opcode.STSFLD0, // store A 210 []any{opcode.CALL, []byte{6}}, opcode.DROP, opcode.RET, // evaluate Unused1 (call to f) and drop its value 211 opcode.LDSFLD0, opcode.RET, // Main 212 opcode.PUSH2, opcode.RET) // f 213 }) 214 t.Run("function as unused struct field", func(t *testing.T) { 215 src := `package foo 216 type Str struct { Int int } 217 var _ = Str{Int: f()} 218 func Main() int { 219 return 1 220 } 221 func f() int { 222 return 2 223 }` 224 eval(t, src, big.NewInt(1), []any{opcode.CALL, []byte{8}}, opcode.PUSH1, opcode.PACKSTRUCT, opcode.DROP, opcode.RET, // evaluate struct val 225 opcode.PUSH1, opcode.RET, // Main 226 opcode.PUSH2, opcode.RET) // f 227 }) 228 t.Run("used in unused function", func(t *testing.T) { 229 src := `package foo 230 var Unused1, Unused2, Unused3 = 1, 2, 3 231 func Main() int { 232 return 1 233 } 234 func unused1() int { 235 return Unused1 236 } 237 func unused2() int { 238 return Unused1 + unused1() 239 } 240 func unused3() int { 241 return Unused2 + unused2() 242 }` 243 prog := eval(t, src, big.NewInt(1)) 244 require.Equal(t, 2, len(prog)) // Main 245 }) 246 t.Run("used in used function", func(t *testing.T) { 247 src := `package foo 248 var A = 1 249 func Main() int { 250 return f() 251 } 252 func f() int { 253 return A 254 }` 255 eval(t, src, big.NewInt(1)) 256 checkInstrCount(t, src, 1, 1, 0, 0) 257 }) 258 t.Run("unused, initialized via init", func(t *testing.T) { 259 src := `package foo 260 var A int 261 func Main() int { 262 return 2 263 } 264 func init() { 265 A = 1 // Although A is unused from exported functions, it's used from init(), so it should be mark as "used" and stored. 266 }` 267 eval(t, src, big.NewInt(2)) 268 checkInstrCount(t, src, 1, 0, 0, 0) 269 }) 270 t.Run("used, initialized via init", func(t *testing.T) { 271 src := `package foo 272 var A int 273 func Main() int { 274 return A 275 } 276 func init() { 277 A = 1 278 }` 279 eval(t, src, big.NewInt(1)) 280 checkInstrCount(t, src, 1, 0, 0, 0) 281 }) 282 t.Run("unused, initialized by function call", func(t *testing.T) { 283 t.Run("unnamed", func(t *testing.T) { 284 src := `package foo 285 var _ = f() 286 func Main() int { 287 return 1 288 } 289 func f() int { 290 return 2 291 }` 292 eval(t, src, big.NewInt(1)) 293 checkInstrCount(t, src, 0, 1, 0, 0) 294 }) 295 t.Run("named", func(t *testing.T) { 296 src := `package foo 297 var A = f() 298 func Main() int { 299 return 1 300 } 301 func f() int { 302 return 2 303 }` 304 eval(t, src, big.NewInt(1)) 305 checkInstrCount(t, src, 0, 1, 0, 0) 306 }) 307 t.Run("named, with dependency on unused var", func(t *testing.T) { 308 src := `package foo 309 var ( 310 A = 1 311 B = A + 1 // To check nested ident values. 312 C = 3 313 D = B + f() + C // To check that both idents (before and after the call to f) will be marked as "used". 314 E = C + 1 // Unused, no code expected. 315 ) 316 func Main() int { 317 return 1 318 } 319 func f() int { 320 return 2 321 }` 322 eval(t, src, big.NewInt(1), []any{opcode.INITSSLOT, []byte{3}}, // sslot for A 323 opcode.PUSH1, opcode.STSFLD0, // store A 324 opcode.LDSFLD0, opcode.PUSH1, opcode.ADD, opcode.STSFLD1, // store B 325 opcode.PUSH3, opcode.STSFLD2, // store C 326 opcode.LDSFLD1, []any{opcode.CALL, []byte{9}}, opcode.ADD, opcode.LDSFLD2, opcode.ADD, opcode.DROP, opcode.RET, // evaluate D and drop 327 opcode.PUSH1, opcode.RET, // Main 328 opcode.PUSH2, opcode.RET) // f 329 }) 330 t.Run("named, with dependency on unused var ident inside function call", func(t *testing.T) { 331 src := `package foo 332 var A = 1 333 var B = f(A) 334 func Main() int { 335 return 1 336 } 337 func f(a int) int { 338 return a 339 }` 340 eval(t, src, big.NewInt(1), []any{opcode.INITSSLOT, []byte{1}}, // sslot for A 341 opcode.PUSH1, opcode.STSFLD0, // store A 342 opcode.LDSFLD0, []any{opcode.CALL, []byte{6}}, opcode.DROP, opcode.RET, // evaluate B and drop 343 opcode.PUSH1, opcode.RET, // Main 344 []any{opcode.INITSLOT, []byte{0, 1}}, opcode.LDARG0, opcode.RET) // f 345 }) 346 t.Run("named, inside multi-specs and multi-vals var declaration", func(t *testing.T) { 347 src := `package foo 348 var ( 349 Unused = 1 350 Unused1, A, Unused2 = 2, 3 + f(), 4 351 ) 352 func Main() int { 353 return 1 354 } 355 func f() int { 356 return 5 357 }` 358 eval(t, src, big.NewInt(1), opcode.PUSH3, []any{opcode.CALL, []byte{7}}, opcode.ADD, opcode.DROP, opcode.RET, // evaluate and drop A 359 opcode.PUSH1, opcode.RET, // Main 360 opcode.PUSH5, opcode.RET) // f 361 }) 362 t.Run("unnamed + unused", func(t *testing.T) { 363 src := `package foo 364 var A = 1 // At least one global variable is used, thus, the whole set of package variables will be walked. 365 var B = 2 366 var _ = B + 1 // This variable is unnamed and doesn't contain call, thus its children won't be marked as "used". 367 func Main() int { 368 return A 369 }` 370 eval(t, src, big.NewInt(1), []any{opcode.INITSSLOT, []byte{1}}, // sslot for A 371 opcode.PUSH1, opcode.STSFLD0, opcode.RET, // store A 372 opcode.LDSFLD0, opcode.RET) // Main 373 }) 374 t.Run("mixed value", func(t *testing.T) { 375 src := `package foo 376 var control int // At least one global variable is used, thus the whole set of package variables will be walked. 377 var B = 2 378 var _ = 1 + f() + B // This variable is unnamed but contains call, thus its children will be marked as "used". 379 func Main() int { 380 return control 381 } 382 func f() int { 383 control = 1 384 return 3 385 }` 386 eval(t, src, big.NewInt(1)) 387 checkInstrCount(t, src, 2 /* control + B */, 1, 0, 0) 388 }) 389 t.Run("multiple function return values", func(t *testing.T) { 390 src := `package foo 391 var A, B = f() 392 func Main() int { 393 return A 394 } 395 func f() (int, int) { 396 return 3, 4 397 }` 398 eval(t, src, big.NewInt(3), []any{opcode.INITSSLOT, []byte{1}}, // sslot for A 399 []any{opcode.CALL, []byte{7}}, opcode.STSFLD0, opcode.DROP, opcode.RET, // evaluate and store A, drop B 400 opcode.LDSFLD0, opcode.RET, // Main 401 opcode.PUSH4, opcode.PUSH3, opcode.RET) // f 402 }) 403 t.Run("constant in declaration", func(t *testing.T) { 404 src := `package foo 405 const A = 5 406 var Unused = 1 + A 407 func Main() int { 408 return 1 409 }` 410 prog := eval(t, src, big.NewInt(1)) 411 require.Equal(t, 2, len(prog)) // Main 412 }) 413 t.Run("mixed expression", func(t *testing.T) { 414 src := `package foo 415 type CustomInt struct { 416 Int int 417 } 418 var A = CustomInt{Int: 2} 419 var B = f(3) + A.f(1) 420 func Main() int { 421 return 1 422 } 423 func f(a int) int { 424 return a 425 } 426 func (i CustomInt) f(a int) int { // has the same name as f 427 return i.Int + a 428 }` 429 eval(t, src, big.NewInt(1)) 430 checkInstrCount(t, src, 1 /* A */, 2, 2, 0) 431 }) 432 }) 433 t.Run("mixed nested expressions", func(t *testing.T) { 434 src := `package foo 435 type CustomInt struct { Int int} // has the same field name as Int variable, important for test 436 var A = CustomInt{Int: 2} 437 var B = f(A.Int) 438 var Unused = 4 439 var Int = 5 // unused and MUST NOT be treated as "used" 440 var C = CustomInt{Int: Unused}.Int + f(1) // uses Unused => Unused should be marked as "used" 441 func Main() int { 442 return 1 443 } 444 func f(a int) int { 445 return a 446 } 447 func (i CustomInt) f(a int) int { // has the same name as f 448 return i.Int + a 449 }` 450 eval(t, src, big.NewInt(1)) 451 }) 452 t.Run("composite literal", func(t *testing.T) { 453 src := `package foo 454 var A = 2 455 var B = []int{1, A, 3}[1] 456 var C = f(1) + B 457 func Main() int { 458 return 1 459 } 460 func f(a int) int { 461 return a 462 }` 463 eval(t, src, big.NewInt(1), []any{opcode.INITSSLOT, []byte{2}}, // sslot for A, B 464 opcode.PUSH2, opcode.STSFLD0, // store A 465 opcode.PUSH3, opcode.LDSFLD0, opcode.PUSH1, opcode.PUSH3, opcode.PACK, opcode.PUSH1, opcode.PICKITEM, opcode.STSFLD1, // evaluate B 466 opcode.PUSH1, []any{opcode.CALL, []byte{8}}, opcode.LDSFLD1, opcode.ADD, opcode.DROP, opcode.RET, // evalute C and drop 467 opcode.PUSH1, opcode.RET, // Main 468 []any{opcode.INITSLOT, []byte{0, 1}}, opcode.LDARG0, opcode.RET) // f 469 }) 470 t.Run("index expression", func(t *testing.T) { 471 src := `package foo 472 var Unused = 2 473 var A = f(1) + []int{1, 2, 3}[Unused] // index expression 474 func Main() int { 475 return 1 476 } 477 func f(a int) int { 478 return a 479 }` 480 eval(t, src, big.NewInt(1), []any{opcode.INITSSLOT, []byte{1}}, // sslot for Unused 481 opcode.PUSH2, opcode.STSFLD0, // store Unused 482 opcode.PUSH1, []any{opcode.CALL, []byte{14}}, // call f(1) 483 opcode.PUSH3, opcode.PUSH2, opcode.PUSH1, opcode.PUSH3, opcode.PACK, opcode.LDSFLD0, opcode.PICKITEM, // eval index expression 484 opcode.ADD, opcode.DROP, opcode.RET, // eval and drop A 485 opcode.PUSH1, opcode.RET, // Main 486 []any{opcode.INITSLOT, []byte{0, 1}}, opcode.LDARG0, opcode.RET) // f(a) 487 }) 488 t.Run("used via nested function calls", func(t *testing.T) { 489 src := `package foo 490 var A = 1 491 func Main() int { 492 return f() 493 } 494 func f() int { 495 return g() 496 } 497 func g() int { 498 return A 499 }` 500 eval(t, src, big.NewInt(1), []any{opcode.INITSSLOT, []byte{1}}, // sslot for A 501 opcode.PUSH1, opcode.STSFLD0, opcode.RET, // store A 502 []any{opcode.CALL, []byte{3}}, opcode.RET, // Main 503 []any{opcode.CALL, []byte{3}}, opcode.RET, // f 504 opcode.LDSFLD0, opcode.RET) // g 505 }) 506 t.Run("struct field name matches global var name", func(t *testing.T) { 507 src := `package foo 508 type CustomStr struct { Int int } 509 var str = CustomStr{Int: 2} 510 var Int = 5 // Unused and the code must not be emitted. 511 func Main() int { 512 return str.Int 513 }` 514 eval(t, src, big.NewInt(2), []any{opcode.INITSSLOT, []byte{1}}, // sslot for str 515 opcode.PUSH2, opcode.PUSH1, opcode.PACKSTRUCT, opcode.STSFLD0, opcode.RET, // store str 516 opcode.LDSFLD0, opcode.PUSH0, opcode.PICKITEM, opcode.RET) // Main 517 }) 518 t.Run("var as a struct field initializer", func(t *testing.T) { 519 src := `package foo 520 type CustomStr struct { Int int } 521 var A = 5 522 var Int = 6 // Unused 523 func Main() int { 524 return CustomStr{Int: A}.Int 525 }` 526 eval(t, src, big.NewInt(5)) 527 }) 528 t.Run("argument of globally called function", func(t *testing.T) { 529 src := `package foo 530 var Unused = 5 531 var control int 532 var _, A = f(Unused) 533 func Main() int { 534 return control 535 } 536 func f(int) (int, int) { 537 control = 5 538 return 1, 2 539 }` 540 eval(t, src, big.NewInt(5)) 541 }) 542 t.Run("argument of locally called function", func(t *testing.T) { 543 src := `package foo 544 var Unused = 5 545 func Main() int { 546 var _, a = f(Unused) 547 return a 548 } 549 func f(i int) (int, int) { 550 return i, i 551 }` 552 eval(t, src, big.NewInt(5)) 553 }) 554 t.Run("used in globally called defer", func(t *testing.T) { 555 src := `package foo 556 var control1, control2 int 557 var Unused = 5 558 var _ = f() 559 func Main() int { 560 return control1 + control2 561 } 562 func f() int { 563 control1 = 1 564 defer func(){ 565 control2 = Unused 566 }() 567 return 2 568 }` 569 eval(t, src, big.NewInt(6)) 570 }) 571 t.Run("used in locally called defer", func(t *testing.T) { 572 src := `package foo 573 var control1, control2 int 574 var Unused = 5 575 func Main() int { 576 _ = f() 577 return control1 + control2 578 } 579 func f() int { 580 control1 = 1 581 defer func(){ 582 control2 = Unused 583 }() 584 return 2 585 }` 586 eval(t, src, big.NewInt(6)) 587 }) 588 t.Run("imported", func(t *testing.T) { 589 t.Run("init by func call", func(t *testing.T) { 590 src := `package foo 591 import "github.com/nspcc-dev/neo-go/pkg/compiler/testdata/globalvar" 592 func Main() int { 593 return globalvar.Default 594 }` 595 eval(t, src, big.NewInt(0)) 596 checkInstrCount(t, src, 1 /* Default */, 1 /* f */, 0, 0) 597 }) 598 t.Run("nested var call", func(t *testing.T) { 599 src := `package foo 600 import "github.com/nspcc-dev/neo-go/pkg/compiler/testdata/globalvar/nested1" 601 func Main() int { 602 return nested1.C 603 }` 604 eval(t, src, big.NewInt(81)) 605 checkInstrCount(t, src, 6 /* dependant vars of nested1.C */, 3, 1, 1) 606 }) 607 t.Run("nested func call", func(t *testing.T) { 608 src := `package foo 609 import "github.com/nspcc-dev/neo-go/pkg/compiler/testdata/globalvar/funccall" 610 func Main() int { 611 return funccall.F() 612 }` 613 eval(t, src, big.NewInt(56)) 614 checkInstrCount(t, src, 2 /* nested2.Argument + nested1.Argument */, -1, -1, -1) 615 }) 616 t.Run("nested method call", func(t *testing.T) { 617 src := `package foo 618 import "github.com/nspcc-dev/neo-go/pkg/compiler/testdata/globalvar/funccall" 619 func Main() int { 620 return funccall.GetAge() 621 }` 622 eval(t, src, big.NewInt(24)) 623 checkInstrCount(t, src, 3, /* nested3.Anna + nested2.Argument + nested3.Argument */ 624 5, /* funccall.GetAge() + Anna.GetAge() + nested1.f + nested1.f + nested2.f */ 625 2 /* nested1.f + nested2.f */, 0) 626 }) 627 }) 628 } 629 630 func TestChangeGlobal(t *testing.T) { 631 t.Run("from Main", func(t *testing.T) { 632 src := `package foo 633 var a int 634 func Main() int { 635 setLocal() 636 set42() 637 setLocal() 638 return a 639 } 640 func set42() { a = 42 } 641 func setLocal() { a := 10; _ = a }` 642 eval(t, src, big.NewInt(42)) 643 }) 644 t.Run("from other global", func(t *testing.T) { 645 t.Skip("see https://github.com/nspcc-dev/neo-go/issues/2661") 646 src := `package foo 647 var A = f() 648 var B int 649 func Main() int { 650 return B 651 } 652 func f() int { 653 B = 3 654 return B 655 }` 656 eval(t, src, big.NewInt(3)) 657 }) 658 } 659 660 func TestMultiDeclaration(t *testing.T) { 661 src := `package foo 662 var a, b, c int 663 func Main() int { 664 a = 1 665 b = 2 666 c = 3 667 return a + b + c 668 }` 669 eval(t, src, big.NewInt(6)) 670 } 671 672 func TestCountLocal(t *testing.T) { 673 src := `package foo 674 func Main() int { 675 a, b, c, d := f() 676 return a + b + c + d 677 } 678 func f() (int, int, int, int) { 679 return 1, 2, 3, 4 680 }` 681 eval(t, src, big.NewInt(10)) 682 } 683 684 func TestMultiDeclarationLocal(t *testing.T) { 685 src := `package foo 686 func Main() int { 687 var a, b, c int 688 a = 1 689 b = 2 690 c = 3 691 return a + b + c 692 }` 693 eval(t, src, big.NewInt(6)) 694 } 695 696 func TestMultiDeclarationLocalCompound(t *testing.T) { 697 src := `package foo 698 func Main() int { 699 var a, b, c []int 700 a = append(a, 1) 701 b = append(b, 2) 702 c = append(c, 3) 703 return a[0] + b[0] + c[0] 704 }` 705 eval(t, src, big.NewInt(6)) 706 } 707 708 func TestShadow(t *testing.T) { 709 srcTmpl := `package foo 710 func Main() int { 711 x := 1 712 y := 10 713 %s 714 x += 1 // increase old local 715 x := 30 // introduce new local 716 y += x // make sure is means something 717 } 718 return x+y 719 }` 720 721 runCase := func(b string) func(t *testing.T) { 722 return func(t *testing.T) { 723 src := fmt.Sprintf(srcTmpl, b) 724 eval(t, src, big.NewInt(42)) 725 } 726 } 727 728 t.Run("If", runCase("if true {")) 729 t.Run("For", runCase("for i := 0; i < 1; i++ {")) 730 t.Run("Range", runCase("for range []int{1} {")) 731 t.Run("Switch", runCase("switch true {\ncase false: x += 2\ncase true:")) 732 t.Run("Block", runCase("{")) 733 } 734 735 func TestArgumentLocal(t *testing.T) { 736 srcTmpl := `package foo 737 func some(a int) int { 738 if a > 42 { 739 a := 24 740 _ = a 741 } 742 return a 743 } 744 func Main() int { 745 return some(%d) 746 }` 747 t.Run("Override", func(t *testing.T) { 748 src := fmt.Sprintf(srcTmpl, 50) 749 eval(t, src, big.NewInt(50)) 750 }) 751 t.Run("NoOverride", func(t *testing.T) { 752 src := fmt.Sprintf(srcTmpl, 40) 753 eval(t, src, big.NewInt(40)) 754 }) 755 } 756 757 func TestContractWithNoMain(t *testing.T) { 758 src := `package foo 759 var someGlobal int = 1 760 func Add3(a int) int { 761 someLocal := 2 762 return someGlobal + someLocal + a 763 }` 764 b, di, err := compiler.CompileWithOptions("foo.go", strings.NewReader(src), nil) 765 require.NoError(t, err) 766 v := vm.New() 767 invokeMethod(t, "Add3", b.Script, v, di) 768 v.Estack().PushVal(39) 769 require.NoError(t, v.Run()) 770 require.Equal(t, 1, v.Estack().Len()) 771 require.Equal(t, big.NewInt(42), v.PopResult()) 772 } 773 774 func TestMultipleFiles(t *testing.T) { 775 src := `package foo 776 import "github.com/nspcc-dev/neo-go/pkg/compiler/testdata/multi" 777 func Main() int { 778 return multi.Sum() 779 }` 780 eval(t, src, big.NewInt(42)) 781 } 782 783 func TestExportedVariable(t *testing.T) { 784 t.Run("Use", func(t *testing.T) { 785 src := `package foo 786 import "github.com/nspcc-dev/neo-go/pkg/compiler/testdata/multi" 787 func Main() int { 788 return multi.SomeVar12 789 }` 790 eval(t, src, big.NewInt(12)) 791 }) 792 t.Run("ChangeAndUse", func(t *testing.T) { 793 src := `package foo 794 import "github.com/nspcc-dev/neo-go/pkg/compiler/testdata/multi" 795 func Main() int { 796 multi.SomeVar12 = 10 797 return multi.Sum() 798 }` 799 eval(t, src, big.NewInt(40)) 800 }) 801 t.Run("PackageAlias", func(t *testing.T) { 802 src := `package foo 803 import kek "github.com/nspcc-dev/neo-go/pkg/compiler/testdata/multi" 804 func Main() int { 805 kek.SomeVar12 = 10 806 return kek.Sum() 807 }` 808 eval(t, src, big.NewInt(40)) 809 }) 810 t.Run("DifferentName", func(t *testing.T) { 811 src := `package foo 812 import "github.com/nspcc-dev/neo-go/pkg/compiler/testdata/strange" 813 func Main() int { 814 normal.NormalVar = 42 815 return normal.NormalVar 816 }` 817 eval(t, src, big.NewInt(42)) 818 }) 819 t.Run("MultipleEqualNames", func(t *testing.T) { 820 src := `package foo 821 import "github.com/nspcc-dev/neo-go/pkg/compiler/testdata/multi" 822 var SomeVar12 = 1 823 func Main() int { 824 SomeVar30 := 3 825 sum := SomeVar12 + multi.SomeVar30 826 sum += SomeVar30 827 sum += multi.SomeVar12 828 return sum 829 }` 830 eval(t, src, big.NewInt(46)) 831 }) 832 } 833 834 func TestExportedConst(t *testing.T) { 835 t.Run("with vars", func(t *testing.T) { 836 src := `package foo 837 import "github.com/nspcc-dev/neo-go/pkg/compiler/testdata/multi" 838 func Main() int { 839 return multi.SomeConst 840 }` 841 eval(t, src, big.NewInt(42)) 842 }) 843 t.Run("const only", func(t *testing.T) { 844 src := `package foo 845 import "github.com/nspcc-dev/neo-go/pkg/compiler/testdata/constonly" 846 func Main() int { 847 return constonly.Answer 848 }` 849 eval(t, src, big.NewInt(42)) 850 }) 851 } 852 853 func TestMultipleFuncSameName(t *testing.T) { 854 t.Run("Simple", func(t *testing.T) { 855 src := `package foo 856 import "github.com/nspcc-dev/neo-go/pkg/compiler/testdata/multi" 857 func Main() int { 858 return multi.Sum() + Sum() 859 } 860 func Sum() int { 861 return 11 862 }` 863 eval(t, src, big.NewInt(53)) 864 }) 865 t.Run("WithMethod", func(t *testing.T) { 866 src := `package foo 867 import "github.com/nspcc-dev/neo-go/pkg/compiler/testdata/foo" 868 type Foo struct{} 869 func (f Foo) Bar() int { return 11 } 870 func Bar() int { return 22 } 871 func Main() int { 872 var a Foo 873 var b foo.Foo 874 return a.Bar() + // 11 875 foo.Bar() + // 1 876 b.Bar() + // 8 877 Bar() // 22 878 }` 879 eval(t, src, big.NewInt(42)) 880 }) 881 } 882 883 func TestConstDontUseSlots(t *testing.T) { 884 const count = 256 885 buf := bytes.NewBufferString("package foo\n") 886 for i := 0; i < count; i++ { 887 buf.WriteString(fmt.Sprintf("const n%d = 1\n", i)) 888 } 889 buf.WriteString("func Main() int { sum := 0\n") 890 for i := 0; i < count; i++ { 891 buf.WriteString(fmt.Sprintf("sum += n%d\n", i)) 892 } 893 buf.WriteString("return sum }") 894 895 src := buf.String() 896 eval(t, src, big.NewInt(count)) 897 } 898 899 func TestUnderscoreVarsDontUseSlots(t *testing.T) { 900 const count = 128 901 buf := bytes.NewBufferString("package foo\n") 902 for i := 0; i < count; i++ { 903 buf.WriteString(fmt.Sprintf("var _, n%d = 1, 1\n", i)) 904 } 905 buf.WriteString("func Main() int { sum := 0\n") 906 for i := 0; i < count; i++ { 907 buf.WriteString(fmt.Sprintf("sum += n%d\n", i)) 908 } 909 buf.WriteString("return sum }") 910 911 src := buf.String() 912 eval(t, src, big.NewInt(count)) 913 } 914 915 func TestUnderscoreGlobalVarDontEmitCode(t *testing.T) { 916 src := `package foo 917 var _ int 918 var _ = 1 919 var ( 920 A = 2 921 _ = A + 3 922 _, B, _ = 4, 5, 6 923 _, C, _ = f(A, B) 924 ) 925 var D = 7 // named unused, after global codegen optimisation no code expected 926 func Main() int { 927 return 1 928 } 929 func f(a, b int) (int, int, int) { 930 return 8, 9, 10 931 }` 932 eval(t, src, big.NewInt(1), []any{opcode.INITSSLOT, []byte{2}}, // sslot for A, B 933 opcode.PUSH2, opcode.STSFLD0, // store A 934 opcode.PUSH5, opcode.STSFLD1, // store B 935 opcode.LDSFLD0, opcode.LDSFLD1, opcode.SWAP, []any{opcode.CALL, []byte{8}}, // evaluate f(A,B) 936 opcode.DROP, opcode.DROP, opcode.DROP, opcode.RET, // drop result of f(A,B) 937 opcode.PUSH1, opcode.RET, // Main 938 []any{opcode.INITSLOT, []byte{0, 2}}, opcode.PUSH10, opcode.PUSH9, opcode.PUSH8, opcode.RET) // f 939 }