package compiler_test import ( "fmt" "math/big" "strings" "testing" "github.com/nspcc-dev/neo-go/pkg/compiler" "github.com/nspcc-dev/neo-go/pkg/vm/opcode" "github.com/stretchr/testify/require" ) func TestSimpleFunctionCall(t *testing.T) { src := ` package testcase func Main() int { x := 10 y := getSomeInteger() return x + y } func getSomeInteger() int { x := 10 return x } ` eval(t, src, big.NewInt(20)) } func TestNotAssignedFunctionCall(t *testing.T) { t.Run("Simple", func(t *testing.T) { src := `package testcase func Main() int { getSomeInteger() getSomeInteger() return 0 } func getSomeInteger() int { return 0 }` eval(t, src, big.NewInt(0)) }) t.Run("If", func(t *testing.T) { src := `package testcase func f() bool { return true } func Main() int { if f() { return 42 } return 0 }` eval(t, src, big.NewInt(42)) }) t.Run("Switch", func(t *testing.T) { src := `package testcase func f() bool { return true } func Main() int { switch true { case f(): return 42 default: return 0 } }` eval(t, src, big.NewInt(42)) }) t.Run("Builtin", func(t *testing.T) { src := `package foo import "github.com/nspcc-dev/neo-go/pkg/interop/util" func Main() int { util.FromAddress("NPAsqZkx9WhNd4P72uhZxBhLinSuNkxfB8") util.FromAddress("NPAsqZkx9WhNd4P72uhZxBhLinSuNkxfB8") return 1 }` eval(t, src, big.NewInt(1)) }) t.Run("Lambda", func(t *testing.T) { src := `package foo func Main() int { f := func() (int, int) { return 1, 2 } f() f() return 42 }` eval(t, src, big.NewInt(42)) }) t.Run("VarDecl", func(t *testing.T) { src := `package foo func foo() []int { return []int{1} } func Main() int { var x = foo() return len(x) }` eval(t, src, big.NewInt(1)) }) } func TestMultipleFunctionCalls(t *testing.T) { src := ` package testcase func Main() int { x := 10 y := getSomeInteger() return x + y } func getSomeInteger() int { x := 10 y := getSomeOtherInt() return x + y } func getSomeOtherInt() int { x := 8 return x } ` eval(t, src, big.NewInt(28)) } func TestFunctionCallWithArgs(t *testing.T) { src := ` package testcase func Main() int { x := 10 y := getSomeInteger(x) return y } func getSomeInteger(x int) int { y := 8 return x + y } ` eval(t, src, big.NewInt(18)) } func TestFunctionCallWithInterfaceType(t *testing.T) { src := ` package testcase func Main() interface{} { x := getSomeInteger(10) return x } func getSomeInteger(x interface{}) interface{} { return x } ` eval(t, src, big.NewInt(10)) } func TestFunctionCallMultiArg(t *testing.T) { src := ` package testcase func Main() int { x := addIntegers(2, 4) return x } func addIntegers(x int, y int) int { return x + y } ` eval(t, src, big.NewInt(6)) } func TestFunctionWithVoidReturn(t *testing.T) { src := ` package testcase func Main() int { x := 2 getSomeInteger() y := 4 return x + y } func getSomeInteger() { %s } ` t.Run("EmptyBody", func(t *testing.T) { src := fmt.Sprintf(src, "") eval(t, src, big.NewInt(6)) }) t.Run("SingleReturn", func(t *testing.T) { src := fmt.Sprintf(src, "return") eval(t, src, big.NewInt(6)) }) } func TestFunctionWithVoidReturnBranch(t *testing.T) { src := ` package testcase func Main() int { x := %t f(x) return 2 } func f(x bool) { if x { return } } ` t.Run("ReturnBranch", func(t *testing.T) { src := fmt.Sprintf(src, true) eval(t, src, big.NewInt(2)) }) t.Run("NoReturn", func(t *testing.T) { src := fmt.Sprintf(src, false) eval(t, src, big.NewInt(2)) }) } func TestFunctionWithMultipleArgumentNames(t *testing.T) { src := `package foo func Main() int { return add(1, 2) } func add(a, b int) int { return a + b }` eval(t, src, big.NewInt(3)) } func TestLocalsCount(t *testing.T) { src := `package foo func f(a, b, c int) int { sum := a for i := 0; i < c; i++ { sum += b } return sum } func Main() int { return f(1, 2, 3) }` eval(t, src, big.NewInt(7)) } func TestVariadic(t *testing.T) { srcTmpl := `package foo func someFunc(a int, b ...int) int { sum := a for i := range b { sum = sum - b[i] } return sum } func Main() int { %s return someFunc(10, %s) }` t.Run("Elements", func(t *testing.T) { src := fmt.Sprintf(srcTmpl, "", "1, 2, 3") eval(t, src, big.NewInt(4)) }) t.Run("Slice", func(t *testing.T) { src := fmt.Sprintf(srcTmpl, "a := []int{1, 2, 3}", "a...") eval(t, src, big.NewInt(4)) }) t.Run("Literal", func(t *testing.T) { src := fmt.Sprintf(srcTmpl, "", "[]int{1, 2, 3}...") eval(t, src, big.NewInt(4)) }) } func TestVariadicMethod(t *testing.T) { src := `package foo type myInt int func (x myInt) someFunc(a int, b ...int) int { sum := int(x) + a for i := range b { sum = sum - b[i] } return sum } func Main() int { x := myInt(38) return x.someFunc(10, 1, 2, 3) }` eval(t, src, big.NewInt(42)) } func TestJumpOptimize(t *testing.T) { src := `package foo func init() { if true {} else {} var a int _ = a } func _deploy(_ interface{}, upd bool) { if true {} else {} t := upd _ = t } func Get1() int { return 1 } func Get2() int { Get1(); Get1(); Get1(); Get1(); return Get1() } func Get3() int { return Get2() } func Main() int { return Get3() }` b, di, err := compiler.CompileWithDebugInfo("", strings.NewReader(src)) require.NoError(t, err) require.Equal(t, 6, len(di.Methods)) for _, mi := range di.Methods { // only _deploy and init have locals here if mi.Name.Name == "_deploy" || mi.Name.Name == "init" { require.Equal(t, b[mi.Range.Start], byte(opcode.INITSLOT)) } require.Equal(t, b[mi.Range.End], byte(opcode.RET)) } } func TestFunctionUnusedParameters(t *testing.T) { src := `package foo func add13(a int, _ int, _1 int, _ int) int { return a + _1 } func Main() int { return add13(1, 10, 100, 1000) }` eval(t, src, big.NewInt(101)) }