1f2d76a1c2
While optimizing jumps, old offsets should be compared with the method offset before optimization, not with the constantly changing value.
292 lines
5.2 KiB
Go
292 lines
5.2 KiB
Go
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))
|
|
})
|
|
}
|
|
|
|
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 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)
|
|
for _, mi := range di.Methods {
|
|
require.Equal(t, b[mi.Range.Start], byte(opcode.INITSLOT))
|
|
require.Equal(t, b[mi.Range.End], byte(opcode.RET))
|
|
}
|
|
}
|