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  }