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  }