2019-12-23 14:05:34 +00:00
|
|
|
package compiler_test
|
2018-04-10 09:45:31 +00:00
|
|
|
|
|
|
|
import (
|
2020-05-06 12:39:25 +00:00
|
|
|
"fmt"
|
2018-04-10 09:45:31 +00:00
|
|
|
"math/big"
|
2020-10-14 11:34:53 +00:00
|
|
|
"strings"
|
2018-04-10 09:45:31 +00:00
|
|
|
"testing"
|
2020-10-14 11:34:53 +00:00
|
|
|
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/compiler"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
|
|
|
"github.com/stretchr/testify/require"
|
2018-04-10 09:45:31 +00:00
|
|
|
)
|
|
|
|
|
2022-07-11 16:28:15 +00:00
|
|
|
func TestReturnValueReceiver(t *testing.T) {
|
|
|
|
t.Run("regular", func(t *testing.T) {
|
|
|
|
src := `package foo
|
|
|
|
import "github.com/nspcc-dev/neo-go/pkg/compiler/testdata/method"
|
|
|
|
|
|
|
|
func Main() int {
|
|
|
|
return method.NewX().GetA()
|
|
|
|
}`
|
|
|
|
eval(t, src, big.NewInt(42))
|
|
|
|
})
|
|
|
|
t.Run("inline", func(t *testing.T) {
|
|
|
|
src := `package foo
|
|
|
|
import "github.com/nspcc-dev/neo-go/pkg/compiler/testdata/inline"
|
|
|
|
|
|
|
|
func Main() int {
|
|
|
|
return inline.NewT().GetN()
|
|
|
|
}`
|
|
|
|
eval(t, src, big.NewInt(42))
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2018-04-10 09:45:31 +00:00
|
|
|
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) {
|
2020-08-24 15:37:39 +00:00
|
|
|
t.Run("Simple", func(t *testing.T) {
|
|
|
|
src := `package testcase
|
2018-04-10 09:45:31 +00:00
|
|
|
func Main() int {
|
|
|
|
getSomeInteger()
|
|
|
|
getSomeInteger()
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
|
|
|
func getSomeInteger() int {
|
|
|
|
return 0
|
2020-08-24 15:37:39 +00:00
|
|
|
}`
|
|
|
|
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
|
2023-03-18 07:44:12 +00:00
|
|
|
import "github.com/nspcc-dev/neo-go/pkg/interop/lib/address"
|
2020-08-24 15:37:39 +00:00
|
|
|
func Main() int {
|
2023-03-18 07:44:12 +00:00
|
|
|
address.ToHash160("NPAsqZkx9WhNd4P72uhZxBhLinSuNkxfB8")
|
|
|
|
address.ToHash160("NPAsqZkx9WhNd4P72uhZxBhLinSuNkxfB8")
|
2020-08-24 15:37:39 +00:00
|
|
|
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))
|
|
|
|
})
|
2020-10-19 07:43:45 +00:00
|
|
|
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))
|
|
|
|
})
|
2018-04-10 09:45:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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))
|
|
|
|
}
|
|
|
|
|
2023-04-03 10:34:24 +00:00
|
|
|
func TestFunctionCallWithAnyKeywordType(t *testing.T) {
|
|
|
|
src := `
|
|
|
|
package testcase
|
|
|
|
func Main() any {
|
|
|
|
x := getSomeInteger(10)
|
|
|
|
return x
|
|
|
|
}
|
|
|
|
|
|
|
|
func getSomeInteger(x any) any {
|
|
|
|
return x
|
|
|
|
}
|
|
|
|
`
|
|
|
|
eval(t, src, big.NewInt(10))
|
|
|
|
}
|
|
|
|
|
2018-04-10 09:45:31 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2020-05-06 12:24:12 +00:00
|
|
|
func getSomeInteger() { %s }
|
2018-04-10 09:45:31 +00:00
|
|
|
`
|
2020-05-06 12:24:12 +00:00
|
|
|
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))
|
|
|
|
})
|
2018-04-10 09:45:31 +00:00
|
|
|
}
|
2020-05-06 12:39:25 +00:00
|
|
|
|
|
|
|
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))
|
|
|
|
})
|
|
|
|
}
|
2020-05-06 15:20:12 +00:00
|
|
|
|
|
|
|
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))
|
|
|
|
}
|
2020-06-26 10:24:17 +00:00
|
|
|
|
|
|
|
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))
|
|
|
|
}
|
2020-06-26 10:30:48 +00:00
|
|
|
|
|
|
|
func TestVariadic(t *testing.T) {
|
2020-06-27 07:39:30 +00:00
|
|
|
srcTmpl := `package foo
|
2020-06-26 10:30:48 +00:00
|
|
|
func someFunc(a int, b ...int) int {
|
|
|
|
sum := a
|
|
|
|
for i := range b {
|
|
|
|
sum = sum - b[i]
|
|
|
|
}
|
|
|
|
return sum
|
|
|
|
}
|
|
|
|
func Main() int {
|
2020-06-27 07:39:30 +00:00
|
|
|
%s
|
|
|
|
return someFunc(10, %s)
|
2020-06-26 10:30:48 +00:00
|
|
|
}`
|
2020-06-27 07:39:30 +00:00
|
|
|
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))
|
|
|
|
})
|
2020-06-26 10:30:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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))
|
|
|
|
}
|
2020-10-14 11:34:53 +00:00
|
|
|
|
|
|
|
func TestJumpOptimize(t *testing.T) {
|
|
|
|
src := `package foo
|
2021-02-11 12:41:49 +00:00
|
|
|
func init() {
|
|
|
|
if true {} else {}
|
|
|
|
var a int
|
|
|
|
_ = a
|
|
|
|
}
|
2023-04-03 10:34:24 +00:00
|
|
|
func _deploy(_ any, upd bool) {
|
2021-02-11 12:41:49 +00:00
|
|
|
if true {} else {}
|
|
|
|
t := upd
|
|
|
|
_ = t
|
|
|
|
}
|
2020-10-14 11:34:53 +00:00
|
|
|
func Get1() int { return 1 }
|
|
|
|
func Get2() int { Get1(); Get1(); Get1(); Get1(); return Get1() }
|
|
|
|
func Get3() int { return Get2() }
|
|
|
|
func Main() int {
|
|
|
|
return Get3()
|
|
|
|
}`
|
2021-12-02 13:36:29 +00:00
|
|
|
b, di, err := compiler.CompileWithOptions("file.go", strings.NewReader(src), nil)
|
2020-10-14 11:34:53 +00:00
|
|
|
require.NoError(t, err)
|
2021-02-11 12:41:49 +00:00
|
|
|
require.Equal(t, 6, len(di.Methods))
|
2020-10-14 11:34:53 +00:00
|
|
|
for _, mi := range di.Methods {
|
2021-04-28 13:10:44 +00:00
|
|
|
// only _deploy and init have locals here
|
|
|
|
if mi.Name.Name == "_deploy" || mi.Name.Name == "init" {
|
2021-12-08 19:33:03 +00:00
|
|
|
require.Equal(t, b.Script[mi.Range.Start], byte(opcode.INITSLOT))
|
2021-04-28 13:10:44 +00:00
|
|
|
}
|
2021-12-08 19:33:03 +00:00
|
|
|
require.Equal(t, b.Script[mi.Range.End], byte(opcode.RET))
|
2020-10-14 11:34:53 +00:00
|
|
|
}
|
|
|
|
}
|
2021-09-30 07:18:31 +00:00
|
|
|
|
|
|
|
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))
|
|
|
|
}
|
2021-09-30 08:48:18 +00:00
|
|
|
|
|
|
|
func TestUnusedFunctions(t *testing.T) {
|
|
|
|
t.Run("only variable", func(t *testing.T) {
|
|
|
|
src := `package foo
|
|
|
|
import "github.com/nspcc-dev/neo-go/pkg/compiler/testdata/nestedcall"
|
|
|
|
func Main() int {
|
|
|
|
return nestedcall.X
|
|
|
|
}`
|
|
|
|
|
2021-12-02 13:36:29 +00:00
|
|
|
b, err := compiler.Compile("foo.go", strings.NewReader(src))
|
2021-09-30 08:48:18 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, 3, len(b)) // PUSHINT8 (42) + RET
|
|
|
|
eval(t, src, big.NewInt(42))
|
|
|
|
})
|
|
|
|
t.Run("imported function", func(t *testing.T) {
|
|
|
|
// Check that import map is set correctly during package traversal.
|
|
|
|
src := `package foo
|
|
|
|
import inner "github.com/nspcc-dev/neo-go/pkg/compiler/testdata/nestedcall"
|
|
|
|
func Main() int {
|
|
|
|
return inner.N()
|
|
|
|
}`
|
|
|
|
|
2021-12-02 13:36:29 +00:00
|
|
|
_, err := compiler.Compile("foo.go", strings.NewReader(src))
|
2021-09-30 08:48:18 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
eval(t, src, big.NewInt(65))
|
|
|
|
})
|
2021-10-25 07:52:15 +00:00
|
|
|
t.Run("method inside of an imported package", func(t *testing.T) {
|
|
|
|
// Check that import map is set correctly during package traversal.
|
|
|
|
src := `package foo
|
|
|
|
import inner "github.com/nspcc-dev/neo-go/pkg/compiler/testdata/nestedcall"
|
|
|
|
func Main() int {
|
|
|
|
var t inner.Token
|
|
|
|
return t.Method()
|
|
|
|
}`
|
|
|
|
|
2021-12-02 13:36:29 +00:00
|
|
|
_, err := compiler.Compile("foo.go", strings.NewReader(src))
|
2021-10-25 07:52:15 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
eval(t, src, big.NewInt(2231))
|
|
|
|
})
|
2021-09-30 08:48:18 +00:00
|
|
|
}
|
compiler: fix nil method receiver handling
An attempt to compile the following code leads to runtime panic:
```
package foo
type CustomInt int
func Main() int {
var i CustomInt
i = 5
return i.Do(2)
}
func (CustomInt) Do(arg int) int {
return arg
}
```
The panic:
```
panic: runtime error: index out of range [0] with length 0 [recovered]
panic: runtime error: index out of range [0] with length 0
goroutine 22 [running]:
testing.tRunner.func1.2({0xa341c0, 0xc0001606d8})
/usr/local/go/src/testing/testing.go:1209 +0x24e
testing.tRunner.func1()
/usr/local/go/src/testing/testing.go:1212 +0x218
panic({0xa341c0, 0xc0001606d8})
/usr/local/go/src/runtime/panic.go:1038 +0x215
github.com/nspcc-dev/neo-go/pkg/compiler.(*codegen).convertFuncDecl(0xc00015e3c0, {0xc753b8, 0xc000152c80}, 0xc000266300, 0x30)
/home/anna/Documents/GitProjects/nspcc-dev/neo-go/pkg/compiler/codegen.go:497 +0x10b3
github.com/nspcc-dev/neo-go/pkg/compiler.(*codegen).compile.func2(0xc000152c80, 0xc00023c410)
/home/anna/Documents/GitProjects/nspcc-dev/neo-go/pkg/compiler/codegen.go:2153 +0x3f8
github.com/nspcc-dev/neo-go/pkg/compiler.(*codegen).ForEachFile.func1(0xc000229b80)
/home/anna/Documents/GitProjects/nspcc-dev/neo-go/pkg/compiler/compiler.go:102 +0x82
github.com/nspcc-dev/neo-go/pkg/compiler.(*codegen).ForEachPackage(0xc00015e3c0, 0xc000189bb0)
/home/anna/Documents/GitProjects/nspcc-dev/neo-go/pkg/compiler/compiler.go:93 +0xc6
github.com/nspcc-dev/neo-go/pkg/compiler.(*codegen).ForEachFile(0x999a20, 0xc000130d80)
/home/anna/Documents/GitProjects/nspcc-dev/neo-go/pkg/compiler/compiler.go:99 +0x45
github.com/nspcc-dev/neo-go/pkg/compiler.(*codegen).compile(0xc00015e3c0, 0xc0002669f0, 0x1)
/home/anna/Documents/GitProjects/nspcc-dev/neo-go/pkg/compiler/codegen.go:2140 +0x445
github.com/nspcc-dev/neo-go/pkg/compiler.codeGen(0xc0002669f0)
/home/anna/Documents/GitProjects/nspcc-dev/neo-go/pkg/compiler/codegen.go:2191 +0x353
github.com/nspcc-dev/neo-go/pkg/compiler.CompileWithOptions({0xa6f39a, 0x50b6b3}, {0xc6d1a0, 0xc0002421e0}, 0x0)
/home/anna/Documents/GitProjects/nspcc-dev/neo-go/pkg/compiler/compiler.go:218 +0x65
github.com/nspcc-dev/neo-go/pkg/compiler_test.vmAndCompileInterop(0x5648df, {0xa9bf23, 0x94})
/home/anna/Documents/GitProjects/nspcc-dev/neo-go/pkg/compiler/vm_test.go:75 +0x113
github.com/nspcc-dev/neo-go/pkg/compiler_test.eval(0xc0002421c0, {0xa9bf23, 0x61be8c7}, {0xa68880, 0xc0002421c0})
/home/anna/Documents/GitProjects/nspcc-dev/neo-go/pkg/compiler/vm_test.go:36 +0x2d
github.com/nspcc-dev/neo-go/pkg/compiler_test.TestUnnamedMethodReceiver(0x4079f9)
/home/anna/Documents/GitProjects/nspcc-dev/neo-go/pkg/compiler/function_call_test.go:400 +0x4f
testing.tRunner(0xc000204b60, 0xbcebb0)
/usr/local/go/src/testing/testing.go:1259 +0x102
created by testing.(*T).Run
/usr/local/go/src/testing/testing.go:1306 +0x35a
```
The solution is to use the same approach as for unnamed function
parameters handling introduced in #2204. (c *funcScope).newVariable is
able to properly handle "_" receiver.
2022-08-17 14:41:11 +00:00
|
|
|
|
|
|
|
func TestUnnamedMethodReceiver(t *testing.T) {
|
|
|
|
src := `package foo
|
|
|
|
type CustomInt int
|
|
|
|
func Main() int {
|
|
|
|
var i CustomInt
|
|
|
|
i = 5
|
|
|
|
return i.Do(2)
|
|
|
|
}
|
|
|
|
func (CustomInt) Do(arg int) int {
|
|
|
|
return arg
|
|
|
|
}`
|
|
|
|
eval(t, src, big.NewInt(2))
|
|
|
|
}
|