github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/compiler/function_call_test.go (about) 1 package compiler_test 2 3 import ( 4 "fmt" 5 "math/big" 6 "strings" 7 "testing" 8 9 "github.com/nspcc-dev/neo-go/pkg/compiler" 10 "github.com/nspcc-dev/neo-go/pkg/vm/opcode" 11 "github.com/stretchr/testify/require" 12 ) 13 14 func TestReturnValueReceiver(t *testing.T) { 15 t.Run("regular", func(t *testing.T) { 16 src := `package foo 17 import "github.com/nspcc-dev/neo-go/pkg/compiler/testdata/method" 18 19 func Main() int { 20 return method.NewX().GetA() 21 }` 22 eval(t, src, big.NewInt(42)) 23 }) 24 t.Run("inline", func(t *testing.T) { 25 src := `package foo 26 import "github.com/nspcc-dev/neo-go/pkg/compiler/testdata/inline" 27 28 func Main() int { 29 return inline.NewT().GetN() 30 }` 31 eval(t, src, big.NewInt(42)) 32 }) 33 } 34 35 func TestSimpleFunctionCall(t *testing.T) { 36 src := ` 37 package testcase 38 func Main() int { 39 x := 10 40 y := getSomeInteger() 41 return x + y 42 } 43 44 func getSomeInteger() int { 45 x := 10 46 return x 47 } 48 ` 49 eval(t, src, big.NewInt(20)) 50 } 51 52 func TestNotAssignedFunctionCall(t *testing.T) { 53 t.Run("Simple", func(t *testing.T) { 54 src := `package testcase 55 func Main() int { 56 getSomeInteger() 57 getSomeInteger() 58 return 0 59 } 60 61 func getSomeInteger() int { 62 return 0 63 }` 64 eval(t, src, big.NewInt(0)) 65 }) 66 t.Run("If", func(t *testing.T) { 67 src := `package testcase 68 func f() bool { return true } 69 func Main() int { 70 if f() { 71 return 42 72 } 73 return 0 74 }` 75 eval(t, src, big.NewInt(42)) 76 }) 77 t.Run("Switch", func(t *testing.T) { 78 src := `package testcase 79 func f() bool { return true } 80 func Main() int { 81 switch true { 82 case f(): 83 return 42 84 default: 85 return 0 86 } 87 }` 88 eval(t, src, big.NewInt(42)) 89 }) 90 t.Run("Builtin", func(t *testing.T) { 91 src := `package foo 92 import "github.com/nspcc-dev/neo-go/pkg/interop/lib/address" 93 func Main() int { 94 address.ToHash160("NPAsqZkx9WhNd4P72uhZxBhLinSuNkxfB8") 95 address.ToHash160("NPAsqZkx9WhNd4P72uhZxBhLinSuNkxfB8") 96 return 1 97 }` 98 eval(t, src, big.NewInt(1)) 99 }) 100 t.Run("Lambda", func(t *testing.T) { 101 src := `package foo 102 func Main() int { 103 f := func() (int, int) { return 1, 2 } 104 f() 105 f() 106 return 42 107 }` 108 eval(t, src, big.NewInt(42)) 109 }) 110 t.Run("VarDecl", func(t *testing.T) { 111 src := `package foo 112 func foo() []int { return []int{1} } 113 func Main() int { 114 var x = foo() 115 return len(x) 116 }` 117 eval(t, src, big.NewInt(1)) 118 }) 119 } 120 121 func TestMultipleFunctionCalls(t *testing.T) { 122 src := ` 123 package testcase 124 func Main() int { 125 x := 10 126 y := getSomeInteger() 127 return x + y 128 } 129 130 func getSomeInteger() int { 131 x := 10 132 y := getSomeOtherInt() 133 return x + y 134 } 135 136 func getSomeOtherInt() int { 137 x := 8 138 return x 139 } 140 ` 141 eval(t, src, big.NewInt(28)) 142 } 143 144 func TestFunctionCallWithArgs(t *testing.T) { 145 src := ` 146 package testcase 147 func Main() int { 148 x := 10 149 y := getSomeInteger(x) 150 return y 151 } 152 153 func getSomeInteger(x int) int { 154 y := 8 155 return x + y 156 } 157 ` 158 eval(t, src, big.NewInt(18)) 159 } 160 161 func TestFunctionCallWithInterfaceType(t *testing.T) { 162 src := ` 163 package testcase 164 func Main() interface{} { 165 x := getSomeInteger(10) 166 return x 167 } 168 169 func getSomeInteger(x interface{}) interface{} { 170 return x 171 } 172 ` 173 eval(t, src, big.NewInt(10)) 174 } 175 176 func TestFunctionCallWithAnyKeywordType(t *testing.T) { 177 src := ` 178 package testcase 179 func Main() any { 180 x := getSomeInteger(10) 181 return x 182 } 183 184 func getSomeInteger(x any) any { 185 return x 186 } 187 ` 188 eval(t, src, big.NewInt(10)) 189 } 190 191 func TestFunctionCallMultiArg(t *testing.T) { 192 src := ` 193 package testcase 194 func Main() int { 195 x := addIntegers(2, 4) 196 return x 197 } 198 199 func addIntegers(x int, y int) int { 200 return x + y 201 } 202 ` 203 eval(t, src, big.NewInt(6)) 204 } 205 206 func TestFunctionWithVoidReturn(t *testing.T) { 207 src := ` 208 package testcase 209 func Main() int { 210 x := 2 211 getSomeInteger() 212 y := 4 213 return x + y 214 } 215 216 func getSomeInteger() { %s } 217 ` 218 t.Run("EmptyBody", func(t *testing.T) { 219 src := fmt.Sprintf(src, "") 220 eval(t, src, big.NewInt(6)) 221 }) 222 t.Run("SingleReturn", func(t *testing.T) { 223 src := fmt.Sprintf(src, "return") 224 eval(t, src, big.NewInt(6)) 225 }) 226 } 227 228 func TestFunctionWithVoidReturnBranch(t *testing.T) { 229 src := ` 230 package testcase 231 func Main() int { 232 x := %t 233 f(x) 234 return 2 235 } 236 237 func f(x bool) { 238 if x { 239 return 240 } 241 } 242 ` 243 t.Run("ReturnBranch", func(t *testing.T) { 244 src := fmt.Sprintf(src, true) 245 eval(t, src, big.NewInt(2)) 246 }) 247 t.Run("NoReturn", func(t *testing.T) { 248 src := fmt.Sprintf(src, false) 249 eval(t, src, big.NewInt(2)) 250 }) 251 } 252 253 func TestFunctionWithMultipleArgumentNames(t *testing.T) { 254 src := `package foo 255 func Main() int { 256 return add(1, 2) 257 } 258 func add(a, b int) int { 259 return a + b 260 }` 261 eval(t, src, big.NewInt(3)) 262 } 263 264 func TestLocalsCount(t *testing.T) { 265 src := `package foo 266 func f(a, b, c int) int { 267 sum := a 268 for i := 0; i < c; i++ { 269 sum += b 270 } 271 return sum 272 } 273 func Main() int { 274 return f(1, 2, 3) 275 }` 276 eval(t, src, big.NewInt(7)) 277 } 278 279 func TestVariadic(t *testing.T) { 280 srcTmpl := `package foo 281 func someFunc(a int, b ...int) int { 282 sum := a 283 for i := range b { 284 sum = sum - b[i] 285 } 286 return sum 287 } 288 func Main() int { 289 %s 290 return someFunc(10, %s) 291 }` 292 t.Run("Elements", func(t *testing.T) { 293 src := fmt.Sprintf(srcTmpl, "", "1, 2, 3") 294 eval(t, src, big.NewInt(4)) 295 }) 296 t.Run("Slice", func(t *testing.T) { 297 src := fmt.Sprintf(srcTmpl, "a := []int{1, 2, 3}", "a...") 298 eval(t, src, big.NewInt(4)) 299 }) 300 t.Run("Literal", func(t *testing.T) { 301 src := fmt.Sprintf(srcTmpl, "", "[]int{1, 2, 3}...") 302 eval(t, src, big.NewInt(4)) 303 }) 304 } 305 306 func TestVariadicMethod(t *testing.T) { 307 src := `package foo 308 type myInt int 309 func (x myInt) someFunc(a int, b ...int) int { 310 sum := int(x) + a 311 for i := range b { 312 sum = sum - b[i] 313 } 314 return sum 315 } 316 func Main() int { 317 x := myInt(38) 318 return x.someFunc(10, 1, 2, 3) 319 }` 320 eval(t, src, big.NewInt(42)) 321 } 322 323 func TestJumpOptimize(t *testing.T) { 324 src := `package foo 325 func init() { 326 if true {} else {} 327 var a int 328 _ = a 329 } 330 func _deploy(_ any, upd bool) { 331 if true {} else {} 332 t := upd 333 _ = t 334 } 335 func Get1() int { return 1 } 336 func Get2() int { Get1(); Get1(); Get1(); Get1(); return Get1() } 337 func Get3() int { return Get2() } 338 func Main() int { 339 return Get3() 340 }` 341 b, di, err := compiler.CompileWithOptions("file.go", strings.NewReader(src), nil) 342 require.NoError(t, err) 343 require.Equal(t, 6, len(di.Methods)) 344 for _, mi := range di.Methods { 345 // only _deploy and init have locals here 346 if mi.Name.Name == "_deploy" || mi.Name.Name == "init" { 347 require.Equal(t, b.Script[mi.Range.Start], byte(opcode.INITSLOT)) 348 } 349 require.Equal(t, b.Script[mi.Range.End], byte(opcode.RET)) 350 } 351 } 352 353 func TestFunctionUnusedParameters(t *testing.T) { 354 src := `package foo 355 func add13(a int, _ int, _1 int, _ int) int { 356 return a + _1 357 } 358 func Main() int { 359 return add13(1, 10, 100, 1000) 360 }` 361 eval(t, src, big.NewInt(101)) 362 } 363 364 func TestUnusedFunctions(t *testing.T) { 365 t.Run("only variable", func(t *testing.T) { 366 src := `package foo 367 import "github.com/nspcc-dev/neo-go/pkg/compiler/testdata/nestedcall" 368 func Main() int { 369 return nestedcall.X 370 }` 371 372 b, err := compiler.Compile("foo.go", strings.NewReader(src)) 373 require.NoError(t, err) 374 require.Equal(t, 3, len(b)) // PUSHINT8 (42) + RET 375 eval(t, src, big.NewInt(42)) 376 }) 377 t.Run("imported function", func(t *testing.T) { 378 // Check that import map is set correctly during package traversal. 379 src := `package foo 380 import inner "github.com/nspcc-dev/neo-go/pkg/compiler/testdata/nestedcall" 381 func Main() int { 382 return inner.N() 383 }` 384 385 _, err := compiler.Compile("foo.go", strings.NewReader(src)) 386 require.NoError(t, err) 387 eval(t, src, big.NewInt(65)) 388 }) 389 t.Run("method inside of an imported package", func(t *testing.T) { 390 // Check that import map is set correctly during package traversal. 391 src := `package foo 392 import inner "github.com/nspcc-dev/neo-go/pkg/compiler/testdata/nestedcall" 393 func Main() int { 394 var t inner.Token 395 return t.Method() 396 }` 397 398 _, err := compiler.Compile("foo.go", strings.NewReader(src)) 399 require.NoError(t, err) 400 eval(t, src, big.NewInt(2231)) 401 }) 402 } 403 404 func TestUnnamedMethodReceiver(t *testing.T) { 405 src := `package foo 406 type CustomInt int 407 func Main() int { 408 var i CustomInt 409 i = 5 410 return i.Do(2) 411 } 412 func (CustomInt) Do(arg int) int { 413 return arg 414 }` 415 eval(t, src, big.NewInt(2)) 416 }