mirror of
https://github.com/nspcc-dev/neo-go.git
synced 2024-11-23 03:38:35 +00:00
Merge pull request #2382 from nspcc-dev/compiler-optimize
compiler: optimize tests
This commit is contained in:
commit
96cd415384
9 changed files with 804 additions and 783 deletions
|
@ -1,16 +1,21 @@
|
|||
package compiler_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/compiler"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var assignTestCases = []testCase{
|
||||
{
|
||||
"chain define",
|
||||
`
|
||||
package foo
|
||||
func Main() int {
|
||||
`func F%d() int {
|
||||
x := 4
|
||||
y := x
|
||||
z := y
|
||||
|
@ -23,9 +28,7 @@ var assignTestCases = []testCase{
|
|||
},
|
||||
{
|
||||
"simple assign",
|
||||
`
|
||||
package foo
|
||||
func Main() int {
|
||||
`func F%d() int {
|
||||
x := 4
|
||||
x = 8
|
||||
return x
|
||||
|
@ -35,9 +38,7 @@ var assignTestCases = []testCase{
|
|||
},
|
||||
{
|
||||
"add assign",
|
||||
`
|
||||
package foo
|
||||
func Main() int {
|
||||
`func F%d() int {
|
||||
x := 4
|
||||
x += 8
|
||||
return x
|
||||
|
@ -47,9 +48,7 @@ var assignTestCases = []testCase{
|
|||
},
|
||||
{
|
||||
"sub assign",
|
||||
`
|
||||
package foo
|
||||
func Main() int {
|
||||
`func F%d() int {
|
||||
x := 4
|
||||
x -= 2
|
||||
return x
|
||||
|
@ -59,9 +58,7 @@ var assignTestCases = []testCase{
|
|||
},
|
||||
{
|
||||
"mul assign",
|
||||
`
|
||||
package foo
|
||||
func Main() int {
|
||||
`func F%d() int {
|
||||
x := 4
|
||||
x *= 2
|
||||
return x
|
||||
|
@ -71,9 +68,7 @@ var assignTestCases = []testCase{
|
|||
},
|
||||
{
|
||||
"div assign",
|
||||
`
|
||||
package foo
|
||||
func Main() int {
|
||||
`func F%d() int {
|
||||
x := 4
|
||||
x /= 2
|
||||
return x
|
||||
|
@ -83,9 +78,7 @@ var assignTestCases = []testCase{
|
|||
},
|
||||
{
|
||||
"add assign binary expr",
|
||||
`
|
||||
package foo
|
||||
func Main() int {
|
||||
`func F%d() int {
|
||||
x := 4
|
||||
x += 6 + 2
|
||||
return x
|
||||
|
@ -95,9 +88,7 @@ var assignTestCases = []testCase{
|
|||
},
|
||||
{
|
||||
"add assign binary expr ident",
|
||||
`
|
||||
package foo
|
||||
func Main() int {
|
||||
`func F%d() int {
|
||||
x := 4
|
||||
y := 5
|
||||
x += 6 + y
|
||||
|
@ -108,19 +99,17 @@ var assignTestCases = []testCase{
|
|||
},
|
||||
{
|
||||
"add assign for string",
|
||||
`package foo
|
||||
func Main() string {
|
||||
`func F%d() string {
|
||||
s := "Hello, "
|
||||
s += "world!"
|
||||
return s
|
||||
}`,
|
||||
}
|
||||
`,
|
||||
[]byte("Hello, world!"),
|
||||
},
|
||||
{
|
||||
"decl assign",
|
||||
`
|
||||
package foo
|
||||
func Main() int {
|
||||
`func F%d() int {
|
||||
var x int = 4
|
||||
return x
|
||||
}
|
||||
|
@ -129,32 +118,41 @@ var assignTestCases = []testCase{
|
|||
},
|
||||
{
|
||||
"multi assign",
|
||||
`
|
||||
package foo
|
||||
func Main() int {
|
||||
`func F%d() int {
|
||||
x, y := 1, 2
|
||||
return x + y
|
||||
}
|
||||
`,
|
||||
big.NewInt(3),
|
||||
},
|
||||
{
|
||||
"many assignments",
|
||||
`func F%d() int {
|
||||
a := 0
|
||||
` + strings.Repeat("a += 1\n", 1024) + `
|
||||
return a
|
||||
}
|
||||
`,
|
||||
big.NewInt(1024),
|
||||
},
|
||||
}
|
||||
|
||||
func TestAssignments(t *testing.T) {
|
||||
runTestCases(t, assignTestCases)
|
||||
}
|
||||
|
||||
func TestManyAssignments(t *testing.T) {
|
||||
src1 := `package foo
|
||||
func Main() int {
|
||||
a := 0
|
||||
`
|
||||
src2 := `return a
|
||||
}`
|
||||
|
||||
for i := 0; i < 1024; i++ {
|
||||
src1 += "a += 1\n"
|
||||
srcBuilder := bytes.NewBuffer([]byte("package testcase\n"))
|
||||
for i, tc := range assignTestCases {
|
||||
srcBuilder.WriteString(fmt.Sprintf(tc.src, i))
|
||||
}
|
||||
|
||||
eval(t, src1+src2, big.NewInt(1024))
|
||||
ne, di, err := compiler.CompileWithOptions("file.go", strings.NewReader(srcBuilder.String()), nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
for i, tc := range assignTestCases {
|
||||
v := vm.New()
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
v.Istack().Clear()
|
||||
v.Estack().Clear()
|
||||
invokeMethod(t, fmt.Sprintf("F%d", i), ne.Script, v, di)
|
||||
runAndCheck(t, v, tc.result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,9 +15,7 @@ import (
|
|||
var binaryExprTestCases = []testCase{
|
||||
{
|
||||
"simple add",
|
||||
`
|
||||
package testcase
|
||||
func Main() int {
|
||||
`func F%d() int {
|
||||
x := 2 + 2
|
||||
return x
|
||||
}
|
||||
|
@ -26,9 +24,7 @@ var binaryExprTestCases = []testCase{
|
|||
},
|
||||
{
|
||||
"simple sub",
|
||||
`
|
||||
package testcase
|
||||
func Main() int {
|
||||
`func F%d() int {
|
||||
x := 2 - 2
|
||||
return x
|
||||
}
|
||||
|
@ -37,9 +33,7 @@ var binaryExprTestCases = []testCase{
|
|||
},
|
||||
{
|
||||
"simple div",
|
||||
`
|
||||
package testcase
|
||||
func Main() int {
|
||||
`func F%d() int {
|
||||
x := 2 / 2
|
||||
return x
|
||||
}
|
||||
|
@ -48,10 +42,8 @@ var binaryExprTestCases = []testCase{
|
|||
},
|
||||
{
|
||||
"simple mod",
|
||||
`
|
||||
package testcase
|
||||
func Main() int {
|
||||
x := 3 % 2
|
||||
`func F%d() int {
|
||||
x := 3 %% 2
|
||||
return x
|
||||
}
|
||||
`,
|
||||
|
@ -59,9 +51,7 @@ var binaryExprTestCases = []testCase{
|
|||
},
|
||||
{
|
||||
"simple mul",
|
||||
`
|
||||
package testcase
|
||||
func Main() int {
|
||||
`func F%d() int {
|
||||
x := 4 * 2
|
||||
return x
|
||||
}
|
||||
|
@ -70,9 +60,7 @@ var binaryExprTestCases = []testCase{
|
|||
},
|
||||
{
|
||||
"simple binary expr in return",
|
||||
`
|
||||
package testcase
|
||||
func Main() int {
|
||||
`func F%d() int {
|
||||
x := 2
|
||||
return 2 + x
|
||||
}
|
||||
|
@ -81,9 +69,7 @@ var binaryExprTestCases = []testCase{
|
|||
},
|
||||
{
|
||||
"complex binary expr",
|
||||
`
|
||||
package testcase
|
||||
func Main() int {
|
||||
`func F%d() int {
|
||||
x := 4
|
||||
y := 8
|
||||
z := x + 2 + 2 - 8
|
||||
|
@ -94,9 +80,7 @@ var binaryExprTestCases = []testCase{
|
|||
},
|
||||
{
|
||||
"compare not equal strings with eql",
|
||||
`
|
||||
package testcase
|
||||
func Main() int {
|
||||
`func F%d() int {
|
||||
str := "a string"
|
||||
if str == "another string" {
|
||||
return 1
|
||||
|
@ -108,9 +92,7 @@ var binaryExprTestCases = []testCase{
|
|||
},
|
||||
{
|
||||
"compare equal strings with eql",
|
||||
`
|
||||
package testcase
|
||||
func Main() int {
|
||||
`func F%d() int {
|
||||
str := "a string"
|
||||
if str == "a string" {
|
||||
return 1
|
||||
|
@ -122,9 +104,7 @@ var binaryExprTestCases = []testCase{
|
|||
},
|
||||
{
|
||||
"compare not equal strings with neq",
|
||||
`
|
||||
package testcase
|
||||
func Main() int {
|
||||
`func F%d() int {
|
||||
str := "a string"
|
||||
if str != "another string" {
|
||||
return 1
|
||||
|
@ -136,9 +116,7 @@ var binaryExprTestCases = []testCase{
|
|||
},
|
||||
{
|
||||
"compare equal strings with neq",
|
||||
`
|
||||
package testcase
|
||||
func Main() int {
|
||||
`func F%d() int {
|
||||
str := "a string"
|
||||
if str != "a string" {
|
||||
return 1
|
||||
|
@ -150,9 +128,7 @@ var binaryExprTestCases = []testCase{
|
|||
},
|
||||
{
|
||||
"compare equal ints with eql",
|
||||
`
|
||||
package testcase
|
||||
func Main() int {
|
||||
`func F%d() int {
|
||||
x := 10
|
||||
if x == 10 {
|
||||
return 1
|
||||
|
@ -164,9 +140,7 @@ var binaryExprTestCases = []testCase{
|
|||
},
|
||||
{
|
||||
"compare equal ints with neq",
|
||||
`
|
||||
package testcase
|
||||
func Main() int {
|
||||
`func F%d() int {
|
||||
x := 10
|
||||
if x != 10 {
|
||||
return 1
|
||||
|
@ -178,9 +152,7 @@ var binaryExprTestCases = []testCase{
|
|||
},
|
||||
{
|
||||
"compare not equal ints with eql",
|
||||
`
|
||||
package testcase
|
||||
func Main() int {
|
||||
`func F%d() int {
|
||||
x := 11
|
||||
if x == 10 {
|
||||
return 1
|
||||
|
@ -192,9 +164,7 @@ var binaryExprTestCases = []testCase{
|
|||
},
|
||||
{
|
||||
"compare not equal ints with neq",
|
||||
`
|
||||
package testcase
|
||||
func Main() int {
|
||||
`func F%d() int {
|
||||
x := 11
|
||||
if x != 10 {
|
||||
return 1
|
||||
|
@ -206,9 +176,7 @@ var binaryExprTestCases = []testCase{
|
|||
},
|
||||
{
|
||||
"simple add and assign",
|
||||
`
|
||||
package testcase
|
||||
func Main() int {
|
||||
`func F%d() int {
|
||||
x := 2
|
||||
x += 1
|
||||
return x
|
||||
|
@ -218,9 +186,7 @@ var binaryExprTestCases = []testCase{
|
|||
},
|
||||
{
|
||||
"simple sub and assign",
|
||||
`
|
||||
package testcase
|
||||
func Main() int {
|
||||
`func F%d() int {
|
||||
x := 2
|
||||
x -= 1
|
||||
return x
|
||||
|
@ -230,9 +196,7 @@ var binaryExprTestCases = []testCase{
|
|||
},
|
||||
{
|
||||
"simple mul and assign",
|
||||
`
|
||||
package testcase
|
||||
func Main() int {
|
||||
`func F%d() int {
|
||||
x := 2
|
||||
x *= 2
|
||||
return x
|
||||
|
@ -242,9 +206,7 @@ var binaryExprTestCases = []testCase{
|
|||
},
|
||||
{
|
||||
"simple div and assign",
|
||||
`
|
||||
package testcase
|
||||
func Main() int {
|
||||
`func F%d() int {
|
||||
x := 2
|
||||
x /= 2
|
||||
return x
|
||||
|
@ -254,11 +216,9 @@ var binaryExprTestCases = []testCase{
|
|||
},
|
||||
{
|
||||
"simple mod and assign",
|
||||
`
|
||||
package testcase
|
||||
func Main() int {
|
||||
`func F%d() int {
|
||||
x := 5
|
||||
x %= 2
|
||||
x %%= 2
|
||||
return x
|
||||
}
|
||||
`,
|
||||
|
@ -267,7 +227,23 @@ var binaryExprTestCases = []testCase{
|
|||
}
|
||||
|
||||
func TestBinaryExprs(t *testing.T) {
|
||||
runTestCases(t, binaryExprTestCases)
|
||||
srcBuilder := bytes.NewBuffer([]byte("package testcase\n"))
|
||||
for i, tc := range binaryExprTestCases {
|
||||
srcBuilder.WriteString(fmt.Sprintf(tc.src, i))
|
||||
}
|
||||
|
||||
ne, di, err := compiler.CompileWithOptions("file.go", strings.NewReader(srcBuilder.String()), nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
for i, tc := range binaryExprTestCases {
|
||||
v := vm.New()
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
v.Istack().Clear()
|
||||
v.Estack().Clear()
|
||||
invokeMethod(t, fmt.Sprintf("F%d", i), ne.Script, v, di)
|
||||
runAndCheck(t, v, tc.result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func addBoolExprTestFunc(testCases []testCase, b *bytes.Buffer, val bool, cond string) []testCase {
|
||||
|
@ -381,7 +357,7 @@ func TestBooleanExprs(t *testing.T) {
|
|||
}
|
||||
|
||||
t.Run("Triple", func(t *testing.T) {
|
||||
const step = 256 // empirically found value to make script less than 65536 in size
|
||||
const step = 350 // empirically found value to make script less than 65536 in size
|
||||
for start := 0; start < len(triple); start += step {
|
||||
testCases = testCases[:0]
|
||||
srcBuilder.Reset()
|
||||
|
|
|
@ -1,9 +1,15 @@
|
|||
package compiler_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/compiler"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type convertTestCase struct {
|
||||
|
@ -25,12 +31,11 @@ func getFunctionName(typ string) string {
|
|||
}
|
||||
|
||||
func TestConvert(t *testing.T) {
|
||||
srcTmpl := `package foo
|
||||
import "github.com/nspcc-dev/neo-go/pkg/interop/convert"
|
||||
func Main() %s {
|
||||
srcTmpl := `func F%d() %s {
|
||||
arg := %s
|
||||
return convert.To%s(arg)
|
||||
}`
|
||||
}
|
||||
`
|
||||
|
||||
convertTestCases := []convertTestCase{
|
||||
{"bool", "true", true},
|
||||
|
@ -53,11 +58,24 @@ func TestConvert(t *testing.T) {
|
|||
{"[]byte", "[]byte{0, 1, 0}", []byte{0, 1, 0}},
|
||||
}
|
||||
|
||||
for _, tc := range convertTestCases {
|
||||
srcBuilder := bytes.NewBuffer([]byte(`package testcase
|
||||
import "github.com/nspcc-dev/neo-go/pkg/interop/convert"
|
||||
`))
|
||||
for i, tc := range convertTestCases {
|
||||
name := getFunctionName(tc.returnType)
|
||||
t.Run(tc.argValue+"->"+name, func(t *testing.T) {
|
||||
src := fmt.Sprintf(srcTmpl, tc.returnType, tc.argValue, name)
|
||||
eval(t, src, tc.result)
|
||||
srcBuilder.WriteString(fmt.Sprintf(srcTmpl, i, tc.returnType, tc.argValue, name))
|
||||
}
|
||||
|
||||
ne, di, err := compiler.CompileWithOptions("file.go", strings.NewReader(srcBuilder.String()), nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
for i, tc := range convertTestCases {
|
||||
v := vm.New()
|
||||
t.Run(tc.argValue+getFunctionName(tc.returnType), func(t *testing.T) {
|
||||
v.Istack().Clear()
|
||||
v.Estack().Clear()
|
||||
invokeMethod(t, fmt.Sprintf("F%d", i), ne.Script, v, di)
|
||||
runAndCheck(t, v, tc.result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,16 @@
|
|||
package compiler_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/compiler"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestEntryPointWithMethod(t *testing.T) {
|
||||
|
@ -370,405 +375,368 @@ func TestDec(t *testing.T) {
|
|||
eval(t, src, big.NewInt(1))
|
||||
}
|
||||
|
||||
func TestForLoopEmpty(t *testing.T) {
|
||||
src := `
|
||||
package foo
|
||||
func Main() int {
|
||||
x := 0
|
||||
for {
|
||||
x++
|
||||
if x == 2 {
|
||||
break
|
||||
var forLoopTestCases = []testCase{
|
||||
{
|
||||
"empty for loop",
|
||||
`func F%d() int {
|
||||
x := 0
|
||||
for {
|
||||
x++
|
||||
if x == 2 { break }
|
||||
}
|
||||
return x
|
||||
}
|
||||
return x
|
||||
}
|
||||
`
|
||||
eval(t, src, big.NewInt(2))
|
||||
}
|
||||
|
||||
func TestForLoopBigIter(t *testing.T) {
|
||||
src := `
|
||||
package foo
|
||||
func Main() int {
|
||||
`,
|
||||
big.NewInt(2),
|
||||
}, {
|
||||
"big iteration count",
|
||||
`func F%d() int {
|
||||
x := 0
|
||||
for i := 0; i < 100000; i++ {
|
||||
x = i
|
||||
}
|
||||
return x
|
||||
}
|
||||
`
|
||||
eval(t, src, big.NewInt(99999))
|
||||
}
|
||||
|
||||
func TestForLoopNoInit(t *testing.T) {
|
||||
src := `
|
||||
package foo
|
||||
func Main() int {
|
||||
`,
|
||||
big.NewInt(99999),
|
||||
},
|
||||
{
|
||||
"no init",
|
||||
`func F%d() int {
|
||||
i := 0
|
||||
for ; i < 10; i++ {
|
||||
}
|
||||
return i
|
||||
}
|
||||
`
|
||||
eval(t, src, big.NewInt(10))
|
||||
}
|
||||
|
||||
func TestForLoopNoPost(t *testing.T) {
|
||||
src := `
|
||||
package foo
|
||||
func Main() int {
|
||||
`,
|
||||
big.NewInt(10),
|
||||
},
|
||||
{
|
||||
"no post",
|
||||
`func F%d() int {
|
||||
i := 0
|
||||
for i < 10 {
|
||||
i++
|
||||
}
|
||||
return i
|
||||
}
|
||||
`
|
||||
eval(t, src, big.NewInt(10))
|
||||
}
|
||||
|
||||
func TestForLoopRange(t *testing.T) {
|
||||
src := `
|
||||
package foo
|
||||
func Main() int {
|
||||
sum := 0
|
||||
arr := []int{1, 2, 3}
|
||||
for i := range arr {
|
||||
sum += arr[i]
|
||||
}
|
||||
return sum
|
||||
}`
|
||||
|
||||
eval(t, src, big.NewInt(6))
|
||||
}
|
||||
|
||||
func TestForLoopRangeGlobalIndex(t *testing.T) {
|
||||
src := `
|
||||
package foo
|
||||
func Main() int {
|
||||
sum := 0
|
||||
i := 0
|
||||
arr := []int{1, 2, 3}
|
||||
for i = range arr {
|
||||
sum += arr[i]
|
||||
}
|
||||
return sum + i
|
||||
}`
|
||||
|
||||
eval(t, src, big.NewInt(8))
|
||||
}
|
||||
|
||||
func TestForLoopRangeChangeVariable(t *testing.T) {
|
||||
src := `
|
||||
package foo
|
||||
func Main() int {
|
||||
sum := 0
|
||||
arr := []int{1, 2, 3}
|
||||
for i := range arr {
|
||||
sum += arr[i]
|
||||
i++
|
||||
sum += i
|
||||
}
|
||||
return sum
|
||||
}`
|
||||
|
||||
eval(t, src, big.NewInt(12))
|
||||
}
|
||||
|
||||
func TestForLoopBreak(t *testing.T) {
|
||||
src := `
|
||||
package foo
|
||||
func Main() int {
|
||||
var i int
|
||||
for i < 10 {
|
||||
i++
|
||||
if i == 5 {
|
||||
break
|
||||
`,
|
||||
big.NewInt(10),
|
||||
},
|
||||
{
|
||||
"range",
|
||||
`func F%d() int {
|
||||
sum := 0
|
||||
arr := []int{1, 2, 3}
|
||||
for i := range arr {
|
||||
sum += arr[i]
|
||||
}
|
||||
return sum
|
||||
}
|
||||
return i
|
||||
}`
|
||||
|
||||
eval(t, src, big.NewInt(5))
|
||||
}
|
||||
|
||||
func TestForLoopBreakLabel(t *testing.T) {
|
||||
src := `
|
||||
package foo
|
||||
func Main() int {
|
||||
var i int
|
||||
loop:
|
||||
for i < 10 {
|
||||
i++
|
||||
if i == 5 {
|
||||
break loop
|
||||
`,
|
||||
big.NewInt(6),
|
||||
},
|
||||
{
|
||||
"range, global index",
|
||||
`func F%d() int {
|
||||
sum := 0
|
||||
i := 0
|
||||
arr := []int{1, 2, 3}
|
||||
for i = range arr {
|
||||
sum += arr[i]
|
||||
}
|
||||
return sum + i
|
||||
}
|
||||
return i
|
||||
}`
|
||||
|
||||
eval(t, src, big.NewInt(5))
|
||||
}
|
||||
|
||||
func TestForLoopNestedBreak(t *testing.T) {
|
||||
src := `
|
||||
package foo
|
||||
func Main() int {
|
||||
var i int
|
||||
for i < 10 {
|
||||
i++
|
||||
for j := 0; j < 2; j++ {
|
||||
`,
|
||||
big.NewInt(8),
|
||||
},
|
||||
{
|
||||
"range, change variable",
|
||||
`func F%d() int {
|
||||
sum := 0
|
||||
arr := []int{1, 2, 3}
|
||||
for i := range arr {
|
||||
sum += arr[i]
|
||||
i++
|
||||
sum += i
|
||||
}
|
||||
return sum
|
||||
}
|
||||
`,
|
||||
big.NewInt(12),
|
||||
},
|
||||
{
|
||||
"break",
|
||||
`func F%d() int {
|
||||
var i int
|
||||
for i < 10 {
|
||||
i++
|
||||
if i == 5 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return i
|
||||
}
|
||||
return i
|
||||
}`
|
||||
|
||||
eval(t, src, big.NewInt(11))
|
||||
}
|
||||
|
||||
func TestForLoopNestedBreakLabel(t *testing.T) {
|
||||
src := `
|
||||
package foo
|
||||
func Main() int {
|
||||
var i int
|
||||
loop:
|
||||
for i < 10 {
|
||||
i++
|
||||
for j := 0; j < 2; j++ {
|
||||
`,
|
||||
big.NewInt(5),
|
||||
},
|
||||
{
|
||||
"break label",
|
||||
`func F%d() int {
|
||||
var i int
|
||||
loop:
|
||||
for i < 10 {
|
||||
i++
|
||||
if i == 5 {
|
||||
break loop
|
||||
}
|
||||
}
|
||||
return i
|
||||
}
|
||||
`,
|
||||
big.NewInt(5),
|
||||
},
|
||||
{
|
||||
"nested break",
|
||||
`func F%d() int {
|
||||
var i int
|
||||
for i < 10 {
|
||||
i++
|
||||
for j := 0; j < 2; j++ {
|
||||
i++
|
||||
if i == 5 {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return i
|
||||
}
|
||||
return i
|
||||
}`
|
||||
|
||||
eval(t, src, big.NewInt(5))
|
||||
}
|
||||
|
||||
func TestForLoopContinue(t *testing.T) {
|
||||
src := `
|
||||
package foo
|
||||
func Main() int {
|
||||
var i, j int
|
||||
for i < 10 {
|
||||
i++
|
||||
if i >= 5 {
|
||||
continue
|
||||
`,
|
||||
big.NewInt(11),
|
||||
},
|
||||
{
|
||||
"nested break label",
|
||||
`func F%d() int {
|
||||
var i int
|
||||
loop:
|
||||
for i < 10 {
|
||||
i++
|
||||
for j := 0; j < 2; j++ {
|
||||
if i == 5 {
|
||||
break loop
|
||||
}
|
||||
i++
|
||||
}
|
||||
}
|
||||
j++
|
||||
return i
|
||||
}
|
||||
return j
|
||||
}`
|
||||
|
||||
eval(t, src, big.NewInt(4))
|
||||
}
|
||||
|
||||
func TestForLoopContinueLabel(t *testing.T) {
|
||||
src := `
|
||||
package foo
|
||||
func Main() int {
|
||||
var i, j int
|
||||
loop:
|
||||
for i < 10 {
|
||||
i++
|
||||
if i >= 5 {
|
||||
continue loop
|
||||
}
|
||||
j++
|
||||
}
|
||||
return j
|
||||
}`
|
||||
|
||||
eval(t, src, big.NewInt(4))
|
||||
}
|
||||
|
||||
func TestForLoopNestedContinue(t *testing.T) {
|
||||
src := `
|
||||
package foo
|
||||
func Main() int {
|
||||
var i, k int
|
||||
for i < 10 {
|
||||
i++
|
||||
for j := 0; j < 3; j++ {
|
||||
if j >= 2 {
|
||||
`,
|
||||
big.NewInt(5),
|
||||
},
|
||||
{
|
||||
"continue",
|
||||
`func F%d() int {
|
||||
var i, j int
|
||||
for i < 10 {
|
||||
i++
|
||||
if i >= 5 {
|
||||
continue
|
||||
}
|
||||
k++
|
||||
j++
|
||||
}
|
||||
return j
|
||||
}
|
||||
return k
|
||||
}`
|
||||
|
||||
eval(t, src, big.NewInt(20))
|
||||
}
|
||||
|
||||
func TestForLoopNestedContinueLabel(t *testing.T) {
|
||||
src := `
|
||||
package foo
|
||||
func Main() int {
|
||||
var i int
|
||||
loop:
|
||||
for ; i < 10; i += 10 {
|
||||
i++
|
||||
for j := 0; j < 4; j++ {
|
||||
if i == 5 {
|
||||
`,
|
||||
big.NewInt(4),
|
||||
},
|
||||
{
|
||||
"continue label",
|
||||
`func F%d() int {
|
||||
var i, j int
|
||||
loop:
|
||||
for i < 10 {
|
||||
i++
|
||||
if i >= 5 {
|
||||
continue loop
|
||||
}
|
||||
j++
|
||||
}
|
||||
return j
|
||||
}
|
||||
`,
|
||||
big.NewInt(4),
|
||||
},
|
||||
{
|
||||
"nested continue",
|
||||
`func F%d() int {
|
||||
var i, k int
|
||||
for i < 10 {
|
||||
i++
|
||||
for j := 0; j < 3; j++ {
|
||||
if j >= 2 {
|
||||
continue
|
||||
}
|
||||
k++
|
||||
}
|
||||
}
|
||||
return k
|
||||
}
|
||||
`,
|
||||
big.NewInt(20),
|
||||
},
|
||||
{
|
||||
"nested continue label",
|
||||
`func F%d() int {
|
||||
var i int
|
||||
loop:
|
||||
for ; i < 10; i += 10 {
|
||||
i++
|
||||
for j := 0; j < 4; j++ {
|
||||
if i == 5 {
|
||||
continue loop
|
||||
}
|
||||
i++
|
||||
}
|
||||
}
|
||||
return i
|
||||
}
|
||||
`,
|
||||
big.NewInt(15),
|
||||
},
|
||||
{
|
||||
"range break",
|
||||
`func F%d() int {
|
||||
var i int
|
||||
arr := []int{1, 2, 3}
|
||||
for i = range arr {
|
||||
if arr[i] == 2 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return i
|
||||
}
|
||||
`,
|
||||
big.NewInt(1),
|
||||
},
|
||||
{
|
||||
"range nested break",
|
||||
`func F%d() int {
|
||||
k := 5
|
||||
arr := []int{1, 2, 3}
|
||||
urr := []int{4, 5, 6, 7}
|
||||
loop:
|
||||
for range arr {
|
||||
k++
|
||||
for j := range urr {
|
||||
k++
|
||||
if j == 3 {
|
||||
break loop
|
||||
}
|
||||
}
|
||||
}
|
||||
return k
|
||||
}
|
||||
`,
|
||||
big.NewInt(10),
|
||||
},
|
||||
{
|
||||
"range continue",
|
||||
`func F%d() int {
|
||||
i := 6
|
||||
arr := []int{1, 2, 3}
|
||||
for j := range arr {
|
||||
if arr[j] < 2 {
|
||||
continue
|
||||
}
|
||||
i++
|
||||
}
|
||||
return i
|
||||
}
|
||||
return i
|
||||
}`
|
||||
|
||||
eval(t, src, big.NewInt(15))
|
||||
}
|
||||
|
||||
func TestForLoopRangeBreak(t *testing.T) {
|
||||
src := `
|
||||
package foo
|
||||
func Main() int {
|
||||
var i int
|
||||
arr := []int{1, 2, 3}
|
||||
for i = range arr {
|
||||
if arr[i] == 2 {
|
||||
break
|
||||
`,
|
||||
big.NewInt(8),
|
||||
},
|
||||
{
|
||||
"range, no variable",
|
||||
`func F%d() int {
|
||||
sum := 0
|
||||
arr := []int{1, 2, 3}
|
||||
for range arr {
|
||||
sum += 1
|
||||
}
|
||||
return sum
|
||||
}
|
||||
return i
|
||||
}`
|
||||
|
||||
eval(t, src, big.NewInt(1))
|
||||
}
|
||||
|
||||
func TestForLoopRangeNestedBreak(t *testing.T) {
|
||||
src := `
|
||||
package foo
|
||||
func Main() int {
|
||||
k := 5
|
||||
arr := []int{1, 2, 3}
|
||||
urr := []int{4, 5, 6, 7}
|
||||
loop:
|
||||
for range arr {
|
||||
k++
|
||||
for j := range urr {
|
||||
k++
|
||||
if j == 3 {
|
||||
break loop
|
||||
}
|
||||
`,
|
||||
big.NewInt(3),
|
||||
},
|
||||
{
|
||||
"range value",
|
||||
`func f(a int) int { return a }
|
||||
func F%d() int {
|
||||
var sum int
|
||||
arr := []int{1, 9, 4}
|
||||
for _, v := range arr {
|
||||
sum += f(v)
|
||||
}
|
||||
return sum
|
||||
}
|
||||
return k
|
||||
}`
|
||||
|
||||
eval(t, src, big.NewInt(10))
|
||||
}
|
||||
|
||||
func TestForLoopRangeContinue(t *testing.T) {
|
||||
src := `
|
||||
package foo
|
||||
func Main() int {
|
||||
i := 6
|
||||
arr := []int{1, 2, 3}
|
||||
for j := range arr {
|
||||
if arr[j] < 2 {
|
||||
continue
|
||||
`,
|
||||
big.NewInt(14),
|
||||
},
|
||||
{
|
||||
"range, map",
|
||||
`func F%d() int {
|
||||
m := map[int]int{
|
||||
1: 13,
|
||||
11: 17,
|
||||
}
|
||||
i++
|
||||
var sum int
|
||||
for i, v := range m {
|
||||
sum += i
|
||||
sum += v
|
||||
}
|
||||
return sum
|
||||
}
|
||||
return i
|
||||
}`
|
||||
|
||||
eval(t, src, big.NewInt(8))
|
||||
`,
|
||||
big.NewInt(42),
|
||||
},
|
||||
{
|
||||
"range, type conversion",
|
||||
`type intArr []int
|
||||
func F%d() int {
|
||||
a := []int{1, 2, 3}
|
||||
s := 0
|
||||
for _, v := range intArr(a) {
|
||||
s += v
|
||||
}
|
||||
return s
|
||||
}
|
||||
`,
|
||||
big.NewInt(6),
|
||||
},
|
||||
}
|
||||
|
||||
func TestForLoopRangeNoVariable(t *testing.T) {
|
||||
src := `
|
||||
package foo
|
||||
func Main() int {
|
||||
sum := 0
|
||||
arr := []int{1, 2, 3}
|
||||
for range arr {
|
||||
sum += 1
|
||||
}
|
||||
return sum
|
||||
}`
|
||||
func TestForLoop(t *testing.T) {
|
||||
srcBuilder := bytes.NewBuffer([]byte("package testcase\n"))
|
||||
for i, tc := range forLoopTestCases {
|
||||
srcBuilder.WriteString(fmt.Sprintf(tc.src, i))
|
||||
}
|
||||
|
||||
eval(t, src, big.NewInt(3))
|
||||
}
|
||||
ne, di, err := compiler.CompileWithOptions("file.go", strings.NewReader(srcBuilder.String()), nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
func TestForLoopRangeValue(t *testing.T) {
|
||||
src := `
|
||||
package foo
|
||||
func f(a int) int { return a }
|
||||
func Main() int {
|
||||
var sum int
|
||||
arr := []int{1, 9, 4}
|
||||
for _, v := range arr {
|
||||
sum += f(v)
|
||||
}
|
||||
return sum
|
||||
}`
|
||||
|
||||
eval(t, src, big.NewInt(14))
|
||||
}
|
||||
|
||||
func TestForLoopRangeMap(t *testing.T) {
|
||||
src := `package foo
|
||||
func Main() int {
|
||||
m := map[int]int{
|
||||
1: 13,
|
||||
11: 17,
|
||||
}
|
||||
var sum int
|
||||
for i, v := range m {
|
||||
sum += i
|
||||
sum += v
|
||||
}
|
||||
return sum
|
||||
}`
|
||||
|
||||
eval(t, src, big.NewInt(42))
|
||||
}
|
||||
|
||||
func TestForLoopRangeTypeConversion(t *testing.T) {
|
||||
src := `package foo
|
||||
type intArr []int
|
||||
func Main() int {
|
||||
a := []int{1, 2, 3}
|
||||
s := 0
|
||||
for _, v := range intArr(a) {
|
||||
s += v
|
||||
}
|
||||
return s
|
||||
}`
|
||||
eval(t, src, big.NewInt(6))
|
||||
for i, tc := range forLoopTestCases {
|
||||
v := vm.New()
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
v.Istack().Clear()
|
||||
v.Estack().Clear()
|
||||
invokeMethod(t, fmt.Sprintf("F%d", i), ne.Script, v, di)
|
||||
runAndCheck(t, v, tc.result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestForLoopComplexConditions(t *testing.T) {
|
||||
src := `
|
||||
package foo
|
||||
func Main() int {
|
||||
var ok bool
|
||||
_ = ok
|
||||
i := 0
|
||||
j := 0
|
||||
%s
|
||||
for %s {
|
||||
i++
|
||||
j++
|
||||
%s
|
||||
}
|
||||
return i
|
||||
}`
|
||||
|
||||
tests := []struct {
|
||||
forCondTestCases := []struct {
|
||||
Name string
|
||||
Cond string
|
||||
Assign string
|
||||
|
@ -788,14 +756,39 @@ func TestForLoopComplexConditions(t *testing.T) {
|
|||
{Cond: "ok", Assign: "ok = (i < 2 && j < 4) || (i < 4 && j < 2)", Result: 2},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
tmpl := `func F%d() int {
|
||||
var ok bool
|
||||
_ = ok
|
||||
i := 0
|
||||
j := 0
|
||||
%s
|
||||
for %s {
|
||||
i++
|
||||
j++
|
||||
%s
|
||||
}
|
||||
return i
|
||||
}
|
||||
`
|
||||
srcBuilder := bytes.NewBufferString("package foo\n")
|
||||
for i, tc := range forCondTestCases {
|
||||
srcBuilder.WriteString(fmt.Sprintf(tmpl, i, tc.Assign, tc.Cond, tc.Assign))
|
||||
}
|
||||
|
||||
ne, di, err := compiler.CompileWithOptions("file.go", strings.NewReader(srcBuilder.String()), nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
for i, tc := range forCondTestCases {
|
||||
v := vm.New()
|
||||
name := tc.Cond
|
||||
if tc.Assign != "" {
|
||||
name = tc.Assign
|
||||
}
|
||||
t.Run(name, func(t *testing.T) {
|
||||
s := fmt.Sprintf(src, tc.Assign, tc.Cond, tc.Assign)
|
||||
eval(t, s, big.NewInt(tc.Result))
|
||||
v.Istack().Clear()
|
||||
v.Estack().Clear()
|
||||
invokeMethod(t, fmt.Sprintf("F%d", i), ne.Script, v, di)
|
||||
runAndCheck(t, v, big.NewInt(tc.Result))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
package compiler_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
|
@ -24,6 +26,7 @@ import (
|
|||
"github.com/nspcc-dev/neo-go/pkg/interop/native/roles"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/native/std"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/nef"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
@ -198,11 +201,23 @@ func TestNativeHelpersCompile(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func runNativeTestCases(t *testing.T, ctr interop.ContractMD, name string, testCases []nativeTestCase) {
|
||||
func runNativeTestCases(t *testing.T, ctr interop.ContractMD, name string, nativeTestCases []nativeTestCase) {
|
||||
srcBuilder := bytes.NewBuffer([]byte(`package foo
|
||||
import "github.com/nspcc-dev/neo-go/pkg/interop/native/` + name + `"
|
||||
import "github.com/nspcc-dev/neo-go/pkg/interop"
|
||||
var _ interop.Hash160
|
||||
`))
|
||||
for i, tc := range nativeTestCases {
|
||||
addNativeTestCase(t, srcBuilder, ctr, i, name, tc.method, tc.params...)
|
||||
}
|
||||
|
||||
ne, di, err := compiler.CompileWithOptions("file.go", strings.NewReader(srcBuilder.String()), nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Run(ctr.Name, func(t *testing.T) {
|
||||
for _, tc := range testCases {
|
||||
for i, tc := range nativeTestCases {
|
||||
t.Run(tc.method, func(t *testing.T) {
|
||||
runNativeTestCase(t, ctr, name, tc.method, tc.params...)
|
||||
runNativeTestCase(t, ne, di, ctr, i, tc.method, tc.params...)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
@ -233,30 +248,32 @@ func getMethod(t *testing.T, ctr interop.ContractMD, name string, params []strin
|
|||
return md
|
||||
}
|
||||
|
||||
func runNativeTestCase(t *testing.T, ctr interop.ContractMD, name, method string, params ...string) {
|
||||
func addNativeTestCase(t *testing.T, srcBuilder *bytes.Buffer, ctr interop.ContractMD, i int, name, method string, params ...string) {
|
||||
md := getMethod(t, ctr, method, params)
|
||||
isVoid := md.MD.ReturnType == smartcontract.VoidType
|
||||
srcTmpl := `package foo
|
||||
import "github.com/nspcc-dev/neo-go/pkg/interop/native/%s"
|
||||
import "github.com/nspcc-dev/neo-go/pkg/interop"
|
||||
var _ interop.Hash160
|
||||
`
|
||||
if isVoid {
|
||||
srcTmpl += `func Main() { %s.%s(%s) }`
|
||||
srcBuilder.WriteString("func F" + strconv.Itoa(i) + "() ")
|
||||
if !isVoid {
|
||||
srcBuilder.WriteString("interface{} { return ")
|
||||
} else {
|
||||
srcTmpl += `func Main() interface{} { return %s.%s(%s) }`
|
||||
srcBuilder.WriteString("{ ")
|
||||
}
|
||||
methodUpper := strings.ToUpper(method[:1]) + method[1:] // ASCII only
|
||||
methodUpper = strings.ReplaceAll(methodUpper, "Gas", "GAS")
|
||||
methodUpper = strings.ReplaceAll(methodUpper, "Json", "JSON")
|
||||
src := fmt.Sprintf(srcTmpl, name, name, methodUpper, strings.Join(params, ","))
|
||||
srcBuilder.WriteString(name)
|
||||
srcBuilder.WriteRune('.')
|
||||
srcBuilder.WriteString(methodUpper)
|
||||
srcBuilder.WriteRune('(')
|
||||
srcBuilder.WriteString(strings.Join(params, ", "))
|
||||
srcBuilder.WriteString(") }\n")
|
||||
}
|
||||
|
||||
func runNativeTestCase(t *testing.T, b *nef.File, di *compiler.DebugInfo, ctr interop.ContractMD, i int, method string, params ...string) {
|
||||
md := getMethod(t, ctr, method, params)
|
||||
result := getTestStackItem(md.MD.ReturnType)
|
||||
isVoid := md.MD.ReturnType == smartcontract.VoidType
|
||||
|
||||
v := vm.New()
|
||||
v.GasLimit = -1
|
||||
b, di, err := compiler.CompileWithOptions("foo.go", strings.NewReader(src), nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
result := getTestStackItem(md.MD.ReturnType)
|
||||
v.LoadToken = func(id int32) error {
|
||||
t := b.Tokens[id]
|
||||
if t.Hash != ctr.Hash {
|
||||
|
@ -285,7 +302,7 @@ func runNativeTestCase(t *testing.T, ctr interop.ContractMD, name, method string
|
|||
}
|
||||
return nil
|
||||
}
|
||||
invokeMethod(t, testMainIdent, b.Script, v, di)
|
||||
invokeMethod(t, fmt.Sprintf("F%d", i), b.Script, v, di)
|
||||
require.NoError(t, v.Run())
|
||||
if isVoid {
|
||||
require.Equal(t, 0, v.Estack().Len())
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
package compiler_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/compiler"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
@ -13,9 +16,7 @@ import (
|
|||
var sliceTestCases = []testCase{
|
||||
{
|
||||
"constant index",
|
||||
`
|
||||
package foo
|
||||
func Main() int {
|
||||
`func F%d() int {
|
||||
a := []int{0,0}
|
||||
a[1] = 42
|
||||
return a[1]+0
|
||||
|
@ -25,9 +26,7 @@ var sliceTestCases = []testCase{
|
|||
},
|
||||
{
|
||||
"variable index",
|
||||
`
|
||||
package foo
|
||||
func Main() int {
|
||||
`func F%d() int {
|
||||
a := []int{0,0}
|
||||
i := 1
|
||||
a[i] = 42
|
||||
|
@ -38,19 +37,17 @@ var sliceTestCases = []testCase{
|
|||
},
|
||||
{
|
||||
"increase slice element with +=",
|
||||
`package foo
|
||||
func Main() int {
|
||||
`func F%d() int {
|
||||
a := []int{1, 2, 3}
|
||||
a[1] += 40
|
||||
return a[1]
|
||||
}`,
|
||||
}
|
||||
`,
|
||||
big.NewInt(42),
|
||||
},
|
||||
{
|
||||
"complex test",
|
||||
`
|
||||
package foo
|
||||
func Main() int {
|
||||
`func F%d() int {
|
||||
a := []int{1,2,3}
|
||||
x := a[0]
|
||||
a[x] = a[x] + 4
|
||||
|
@ -62,9 +59,7 @@ var sliceTestCases = []testCase{
|
|||
},
|
||||
{
|
||||
"slice literals with variables",
|
||||
`
|
||||
package foo
|
||||
func Main() int {
|
||||
`func F%d() int {
|
||||
elem := 7
|
||||
a := []int{6, elem, 8}
|
||||
return a[1]
|
||||
|
@ -74,9 +69,7 @@ var sliceTestCases = []testCase{
|
|||
},
|
||||
{
|
||||
"slice literals with expressions",
|
||||
`
|
||||
package foo
|
||||
func Main() int {
|
||||
`func F%d() int {
|
||||
elem := []int{3, 7}
|
||||
a := []int{6, elem[1]*2+1, 24}
|
||||
return a[1]
|
||||
|
@ -86,93 +79,88 @@ var sliceTestCases = []testCase{
|
|||
},
|
||||
{
|
||||
"sub-slice with literal bounds",
|
||||
`
|
||||
package foo
|
||||
func Main() []byte {
|
||||
`func F%d() []byte {
|
||||
a := []byte{0, 1, 2, 3}
|
||||
b := a[1:3]
|
||||
return b
|
||||
}`,
|
||||
}
|
||||
`,
|
||||
[]byte{1, 2},
|
||||
},
|
||||
{
|
||||
"sub-slice with constant bounds",
|
||||
`
|
||||
package foo
|
||||
const x = 1
|
||||
`const x = 1
|
||||
const y = 3
|
||||
func Main() []byte {
|
||||
func F%d() []byte {
|
||||
a := []byte{0, 1, 2, 3}
|
||||
b := a[x:y]
|
||||
return b
|
||||
}`,
|
||||
}
|
||||
`,
|
||||
[]byte{1, 2},
|
||||
},
|
||||
{
|
||||
"sub-slice with variable bounds",
|
||||
`
|
||||
package foo
|
||||
func Main() []byte {
|
||||
`func F%d() []byte {
|
||||
a := []byte{0, 1, 2, 3}
|
||||
x := 1
|
||||
y := 3
|
||||
b := a[x:y]
|
||||
return b
|
||||
}`,
|
||||
}
|
||||
`,
|
||||
[]byte{1, 2},
|
||||
},
|
||||
{
|
||||
"sub-slice with no lower bound",
|
||||
`
|
||||
package foo
|
||||
func Main() []byte {
|
||||
`func F%d() []byte {
|
||||
a := []byte{0, 1, 2, 3}
|
||||
b := a[:3]
|
||||
return b
|
||||
}`,
|
||||
}
|
||||
`,
|
||||
[]byte{0, 1, 2},
|
||||
},
|
||||
{
|
||||
"sub-slice with no upper bound",
|
||||
`
|
||||
package foo
|
||||
func Main() []byte {
|
||||
`func F%d() []byte {
|
||||
a := []byte{0, 1, 2, 3}
|
||||
b := a[2:]
|
||||
return b
|
||||
}`,
|
||||
}
|
||||
`,
|
||||
[]byte{2, 3},
|
||||
},
|
||||
{
|
||||
"declare byte slice",
|
||||
`package foo
|
||||
func Main() []byte {
|
||||
`func F%d() []byte {
|
||||
var a []byte
|
||||
a = append(a, 1)
|
||||
a = append(a, 2)
|
||||
return a
|
||||
}`,
|
||||
}
|
||||
`,
|
||||
[]byte{1, 2},
|
||||
},
|
||||
{
|
||||
"append multiple bytes to a slice",
|
||||
`package foo
|
||||
func Main() []byte {
|
||||
`func F%d() []byte {
|
||||
var a []byte
|
||||
a = append(a, 1, 2)
|
||||
return a
|
||||
}`,
|
||||
}
|
||||
`,
|
||||
[]byte{1, 2},
|
||||
},
|
||||
{
|
||||
"append multiple ints to a slice",
|
||||
`package foo
|
||||
func Main() []int {
|
||||
`func F%d() []int {
|
||||
var a []int
|
||||
a = append(a, 1, 2, 3)
|
||||
a = append(a, 4, 5)
|
||||
return a
|
||||
}`,
|
||||
}
|
||||
`,
|
||||
[]stackitem.Item{
|
||||
stackitem.NewBigInteger(big.NewInt(1)),
|
||||
stackitem.NewBigInteger(big.NewInt(2)),
|
||||
|
@ -183,15 +171,15 @@ var sliceTestCases = []testCase{
|
|||
},
|
||||
{
|
||||
"int slice, append slice",
|
||||
`package foo
|
||||
func getByte() byte { return 0x80 }
|
||||
func Main() []int {
|
||||
` func getByte() byte { return 0x80 }
|
||||
func F%d() []int {
|
||||
x := []int{1}
|
||||
y := []int{2, 3}
|
||||
x = append(x, y...)
|
||||
x = append(x, y...)
|
||||
return x
|
||||
}`,
|
||||
}
|
||||
`,
|
||||
[]stackitem.Item{
|
||||
stackitem.Make(1),
|
||||
stackitem.Make(2), stackitem.Make(3),
|
||||
|
@ -200,13 +188,13 @@ var sliceTestCases = []testCase{
|
|||
},
|
||||
{
|
||||
"declare compound slice",
|
||||
`package foo
|
||||
func Main() []string {
|
||||
`func F%d() []string {
|
||||
var a []string
|
||||
a = append(a, "a")
|
||||
a = append(a, "b")
|
||||
return a
|
||||
}`,
|
||||
}
|
||||
`,
|
||||
[]stackitem.Item{
|
||||
stackitem.NewByteArray([]byte("a")),
|
||||
stackitem.NewByteArray([]byte("b")),
|
||||
|
@ -214,14 +202,14 @@ var sliceTestCases = []testCase{
|
|||
},
|
||||
{
|
||||
"declare compound slice alias",
|
||||
`package foo
|
||||
type strs []string
|
||||
func Main() []string {
|
||||
`type strs []string
|
||||
func F%d() []string {
|
||||
var a strs
|
||||
a = append(a, "a")
|
||||
a = append(a, "b")
|
||||
return a
|
||||
}`,
|
||||
}
|
||||
`,
|
||||
[]stackitem.Item{
|
||||
stackitem.NewByteArray([]byte("a")),
|
||||
stackitem.NewByteArray([]byte("b")),
|
||||
|
@ -229,72 +217,70 @@ var sliceTestCases = []testCase{
|
|||
},
|
||||
{
|
||||
"byte-slice assignment",
|
||||
`package foo
|
||||
func Main() []byte {
|
||||
`func F%d() []byte {
|
||||
a := []byte{0, 1, 2}
|
||||
a[1] = 42
|
||||
return a
|
||||
}`,
|
||||
}
|
||||
`,
|
||||
[]byte{0, 42, 2},
|
||||
},
|
||||
{
|
||||
"byte-slice assignment after string conversion",
|
||||
`package foo
|
||||
func Main() []byte {
|
||||
`func F%d() []byte {
|
||||
a := "abc"
|
||||
b := []byte(a)
|
||||
b[1] = 42
|
||||
return []byte(a)
|
||||
}`,
|
||||
}
|
||||
`,
|
||||
[]byte{0x61, 0x62, 0x63},
|
||||
},
|
||||
{
|
||||
"declare and append byte-slice",
|
||||
`package foo
|
||||
func Main() []byte {
|
||||
`func F%d() []byte {
|
||||
var a []byte
|
||||
a = append(a, 1)
|
||||
a = append(a, 2)
|
||||
return a
|
||||
}`,
|
||||
}
|
||||
`,
|
||||
[]byte{1, 2},
|
||||
},
|
||||
{
|
||||
"nested slice assignment",
|
||||
`package foo
|
||||
func Main() int {
|
||||
`func F%d() int {
|
||||
a := [][]int{[]int{1, 2}, []int{3, 4}}
|
||||
a[1][0] = 42
|
||||
return a[1][0]
|
||||
}`,
|
||||
}
|
||||
`,
|
||||
big.NewInt(42),
|
||||
},
|
||||
{
|
||||
"nested slice omitted type (slice)",
|
||||
`package foo
|
||||
func Main() int {
|
||||
`func F%d() int {
|
||||
a := [][]int{{1, 2}, {3, 4}}
|
||||
a[1][0] = 42
|
||||
return a[1][0]
|
||||
}`,
|
||||
}
|
||||
`,
|
||||
big.NewInt(42),
|
||||
},
|
||||
{
|
||||
"nested slice omitted type (struct)",
|
||||
`package foo
|
||||
type pair struct { a, b int }
|
||||
func Main() int {
|
||||
a := []pair{{a: 1, b: 2}, {a: 3, b: 4}}
|
||||
`type pairA struct { a, b int }
|
||||
func F%d() int {
|
||||
a := []pairA{{a: 1, b: 2}, {a: 3, b: 4}}
|
||||
a[1].a = 42
|
||||
return a[1].a
|
||||
}`,
|
||||
}
|
||||
`,
|
||||
big.NewInt(42),
|
||||
},
|
||||
{
|
||||
"defaults to nil for byte slice",
|
||||
`
|
||||
package foo
|
||||
func Main() int {
|
||||
`func F%d() int {
|
||||
var a []byte
|
||||
if a != nil { return 1}
|
||||
return 2
|
||||
|
@ -304,9 +290,7 @@ var sliceTestCases = []testCase{
|
|||
},
|
||||
{
|
||||
"defaults to nil for int slice",
|
||||
`
|
||||
package foo
|
||||
func Main() int {
|
||||
`func F%d() int {
|
||||
var a []int
|
||||
if a != nil { return 1}
|
||||
return 2
|
||||
|
@ -316,11 +300,9 @@ var sliceTestCases = []testCase{
|
|||
},
|
||||
{
|
||||
"defaults to nil for struct slice",
|
||||
`
|
||||
package foo
|
||||
type pair struct { a, b int }
|
||||
func Main() int {
|
||||
var a []pair
|
||||
`type pairB struct { a, b int }
|
||||
func F%d() int {
|
||||
var a []pairB
|
||||
if a != nil { return 1}
|
||||
return 2
|
||||
}
|
||||
|
@ -329,28 +311,42 @@ var sliceTestCases = []testCase{
|
|||
},
|
||||
{
|
||||
"literal byte-slice with variable values",
|
||||
`package foo
|
||||
const sym1 = 's'
|
||||
func Main() []byte {
|
||||
`const sym1 = 's'
|
||||
func F%d() []byte {
|
||||
sym2 := byte('t')
|
||||
sym4 := byte('i')
|
||||
return []byte{sym1, sym2, 'r', sym4, 'n', 'g'}
|
||||
}`,
|
||||
}
|
||||
`,
|
||||
[]byte("string"),
|
||||
},
|
||||
{
|
||||
"literal slice with function call",
|
||||
`package foo
|
||||
func fn() byte { return 't' }
|
||||
func Main() []byte {
|
||||
`func fn() byte { return 't' }
|
||||
func F%d() []byte {
|
||||
return []byte{'s', fn(), 'r'}
|
||||
}`,
|
||||
}
|
||||
`,
|
||||
[]byte("str"),
|
||||
},
|
||||
}
|
||||
|
||||
func TestSliceOperations(t *testing.T) {
|
||||
runTestCases(t, sliceTestCases)
|
||||
srcBuilder := bytes.NewBuffer([]byte("package testcase\n"))
|
||||
for i, tc := range sliceTestCases {
|
||||
srcBuilder.WriteString(fmt.Sprintf(tc.src, i))
|
||||
}
|
||||
|
||||
ne, di, err := compiler.CompileWithOptions("file.go", strings.NewReader(srcBuilder.String()), nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
for i, tc := range sliceTestCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
v := vm.New()
|
||||
invokeMethod(t, fmt.Sprintf("F%d", i), ne.Script, v, di)
|
||||
runAndCheck(t, v, tc.result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestByteSlices(t *testing.T) {
|
||||
|
|
|
@ -1,19 +1,23 @@
|
|||
package compiler_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/compiler"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var structTestCases = []testCase{
|
||||
{
|
||||
"struct field assign",
|
||||
`
|
||||
package foo
|
||||
func Main() int {
|
||||
t := token {
|
||||
`func F%d() int {
|
||||
t := token1 {
|
||||
x: 2,
|
||||
y: 4,
|
||||
}
|
||||
|
@ -22,7 +26,7 @@ var structTestCases = []testCase{
|
|||
return age
|
||||
}
|
||||
|
||||
type token struct {
|
||||
type token1 struct {
|
||||
x int
|
||||
y int
|
||||
}
|
||||
|
@ -31,11 +35,9 @@ var structTestCases = []testCase{
|
|||
},
|
||||
{
|
||||
"struct field from func result",
|
||||
`
|
||||
package foo
|
||||
type S struct { x int }
|
||||
`type S struct { x int }
|
||||
func fn() int { return 2 }
|
||||
func Main() int {
|
||||
func F%d() int {
|
||||
t := S{x: fn()}
|
||||
return t.x
|
||||
}
|
||||
|
@ -44,15 +46,13 @@ var structTestCases = []testCase{
|
|||
},
|
||||
{
|
||||
"struct field return",
|
||||
`
|
||||
package foo
|
||||
type token struct {
|
||||
`type token2 struct {
|
||||
x int
|
||||
y int
|
||||
}
|
||||
|
||||
func Main() int {
|
||||
t := token {
|
||||
func F%d() int {
|
||||
t := token2 {
|
||||
x: 2,
|
||||
y: 4,
|
||||
}
|
||||
|
@ -64,15 +64,13 @@ var structTestCases = []testCase{
|
|||
},
|
||||
{
|
||||
"struct field assign",
|
||||
`
|
||||
package foo
|
||||
type token struct {
|
||||
`type token3 struct {
|
||||
x int
|
||||
y int
|
||||
}
|
||||
|
||||
func Main() int {
|
||||
t := token {
|
||||
func F%d() int {
|
||||
t := token3 {
|
||||
x: 2,
|
||||
y: 4,
|
||||
}
|
||||
|
@ -84,17 +82,15 @@ var structTestCases = []testCase{
|
|||
},
|
||||
{
|
||||
"complex struct",
|
||||
`
|
||||
package foo
|
||||
type token struct {
|
||||
`type token4 struct {
|
||||
x int
|
||||
y int
|
||||
}
|
||||
|
||||
func Main() int {
|
||||
func F%d() int {
|
||||
x := 10
|
||||
|
||||
t := token {
|
||||
t := token4 {
|
||||
x: 2,
|
||||
y: 4,
|
||||
}
|
||||
|
@ -108,95 +104,90 @@ var structTestCases = []testCase{
|
|||
},
|
||||
{
|
||||
"initialize struct field from variable",
|
||||
`
|
||||
package foo
|
||||
type token struct {
|
||||
`type token5 struct {
|
||||
x int
|
||||
y int
|
||||
}
|
||||
|
||||
func Main() int {
|
||||
func F%d() int {
|
||||
x := 10
|
||||
t := token {
|
||||
t := token5 {
|
||||
x: x,
|
||||
y: 4,
|
||||
}
|
||||
y := t.x + t.y
|
||||
return y
|
||||
}`,
|
||||
}
|
||||
`,
|
||||
big.NewInt(14),
|
||||
},
|
||||
{
|
||||
"assign a variable to a struct field",
|
||||
`
|
||||
package foo
|
||||
type token struct {
|
||||
`type token6 struct {
|
||||
x int
|
||||
y int
|
||||
}
|
||||
|
||||
func Main() int {
|
||||
func F%d() int {
|
||||
ten := 10
|
||||
t := token {
|
||||
t := token6 {
|
||||
x: 2,
|
||||
y: 4,
|
||||
}
|
||||
t.x = ten
|
||||
y := t.y + t.x
|
||||
return y
|
||||
}`,
|
||||
}
|
||||
`,
|
||||
big.NewInt(14),
|
||||
},
|
||||
{
|
||||
"increase struct field with +=",
|
||||
`package foo
|
||||
type token struct { x int }
|
||||
func Main() int {
|
||||
t := token{x: 2}
|
||||
`type token7 struct { x int }
|
||||
func F%d() int {
|
||||
t := token7{x: 2}
|
||||
t.x += 3
|
||||
return t.x
|
||||
}`,
|
||||
}
|
||||
`,
|
||||
big.NewInt(5),
|
||||
},
|
||||
{
|
||||
"assign a struct field to a struct field",
|
||||
`
|
||||
package foo
|
||||
type token struct {
|
||||
`type token8 struct {
|
||||
x int
|
||||
y int
|
||||
}
|
||||
|
||||
func Main() int {
|
||||
t1 := token {
|
||||
func F%d() int {
|
||||
t1 := token8 {
|
||||
x: 2,
|
||||
y: 4,
|
||||
}
|
||||
t2 := token {
|
||||
t2 := token8 {
|
||||
x: 3,
|
||||
y: 5,
|
||||
}
|
||||
t1.x = t2.y
|
||||
y := t1.x + t2.x
|
||||
return y
|
||||
}`,
|
||||
}
|
||||
`,
|
||||
big.NewInt(8),
|
||||
},
|
||||
{
|
||||
"initialize same struct twice",
|
||||
`
|
||||
package foo
|
||||
type token struct {
|
||||
`type token9 struct {
|
||||
x int
|
||||
y int
|
||||
}
|
||||
|
||||
func Main() int {
|
||||
t1 := token {
|
||||
func F%d() int {
|
||||
t1 := token9 {
|
||||
x: 2,
|
||||
y: 4,
|
||||
}
|
||||
t2 := token {
|
||||
t2 := token9 {
|
||||
x: 2,
|
||||
y: 4,
|
||||
}
|
||||
|
@ -207,18 +198,16 @@ var structTestCases = []testCase{
|
|||
},
|
||||
{
|
||||
"struct methods",
|
||||
`
|
||||
package foo
|
||||
type token struct {
|
||||
`type token10 struct {
|
||||
x int
|
||||
}
|
||||
|
||||
func(t token) getInteger() int {
|
||||
func(t token10) getInteger() int {
|
||||
return t.x
|
||||
}
|
||||
|
||||
func Main() int {
|
||||
t := token {
|
||||
func F%d() int {
|
||||
t := token10 {
|
||||
x: 4,
|
||||
}
|
||||
someInt := t.getInteger()
|
||||
|
@ -229,19 +218,17 @@ var structTestCases = []testCase{
|
|||
},
|
||||
{
|
||||
"struct methods with arguments",
|
||||
`
|
||||
package foo
|
||||
type token struct {
|
||||
`type token11 struct {
|
||||
x int
|
||||
}
|
||||
|
||||
// Also tests if x conflicts with t.x
|
||||
func(t token) addIntegers(x int, y int) int {
|
||||
func(t token11) addIntegers(x int, y int) int {
|
||||
return t.x + x + y
|
||||
}
|
||||
|
||||
func Main() int {
|
||||
t := token {
|
||||
func F%d() int {
|
||||
t := token11 {
|
||||
x: 4,
|
||||
}
|
||||
someInt := t.addIntegers(2, 4)
|
||||
|
@ -252,17 +239,15 @@ var structTestCases = []testCase{
|
|||
},
|
||||
{
|
||||
"initialize struct partially",
|
||||
`
|
||||
package foo
|
||||
type token struct {
|
||||
`type token12 struct {
|
||||
x int
|
||||
y int
|
||||
z string
|
||||
b bool
|
||||
}
|
||||
|
||||
func Main() int {
|
||||
t := token {
|
||||
func F%d() int {
|
||||
t := token12 {
|
||||
x: 4,
|
||||
}
|
||||
return t.y
|
||||
|
@ -272,17 +257,15 @@ var structTestCases = []testCase{
|
|||
},
|
||||
{
|
||||
"test return struct from func",
|
||||
`
|
||||
package foo
|
||||
type token struct {
|
||||
`type token13 struct {
|
||||
x int
|
||||
y int
|
||||
z string
|
||||
b bool
|
||||
}
|
||||
|
||||
func newToken() token {
|
||||
return token{
|
||||
func newToken() token13 {
|
||||
return token13{
|
||||
x: 1,
|
||||
y: 2,
|
||||
z: "hello",
|
||||
|
@ -290,7 +273,7 @@ var structTestCases = []testCase{
|
|||
}
|
||||
}
|
||||
|
||||
func Main() token {
|
||||
func F%d() token13 {
|
||||
return newToken()
|
||||
}
|
||||
`,
|
||||
|
@ -303,10 +286,7 @@ var structTestCases = []testCase{
|
|||
},
|
||||
{
|
||||
"pass struct as argument",
|
||||
`
|
||||
package foo
|
||||
|
||||
type Bar struct {
|
||||
`type Bar struct {
|
||||
amount int
|
||||
}
|
||||
|
||||
|
@ -315,7 +295,7 @@ var structTestCases = []testCase{
|
|||
return bar.amount
|
||||
}
|
||||
|
||||
func Main() int {
|
||||
func F%d() int {
|
||||
b := Bar{
|
||||
amount: 10,
|
||||
}
|
||||
|
@ -328,110 +308,109 @@ var structTestCases = []testCase{
|
|||
},
|
||||
{
|
||||
"declare struct literal",
|
||||
`package foo
|
||||
func Main() int {
|
||||
`func F%d() int {
|
||||
var x struct {
|
||||
a int
|
||||
}
|
||||
x.a = 2
|
||||
return x.a
|
||||
}`,
|
||||
}
|
||||
`,
|
||||
big.NewInt(2),
|
||||
},
|
||||
{
|
||||
"declare struct type",
|
||||
`package foo
|
||||
type withA struct {
|
||||
`type withA struct {
|
||||
a int
|
||||
}
|
||||
func Main() int {
|
||||
func F%d() int {
|
||||
var x withA
|
||||
x.a = 2
|
||||
return x.a
|
||||
}`,
|
||||
}
|
||||
`,
|
||||
big.NewInt(2),
|
||||
},
|
||||
{
|
||||
"nested selectors (simple read)",
|
||||
`package foo
|
||||
type S1 struct { x, y S2 }
|
||||
`type S1 struct { x, y S2 }
|
||||
type S2 struct { a, b int }
|
||||
func Main() int {
|
||||
func F%d() int {
|
||||
var s1 S1
|
||||
var s2 S2
|
||||
s2.a = 3
|
||||
s1.y = s2
|
||||
return s1.y.a
|
||||
}`,
|
||||
}
|
||||
`,
|
||||
big.NewInt(3),
|
||||
},
|
||||
{
|
||||
"nested selectors (simple write)",
|
||||
`package foo
|
||||
type S1 struct { x S2 }
|
||||
type S2 struct { a int }
|
||||
func Main() int {
|
||||
s1 := S1{
|
||||
x: S2 {
|
||||
`type S3 struct { x S4 }
|
||||
type S4 struct { a int }
|
||||
func F%d() int {
|
||||
s1 := S3{
|
||||
x: S4 {
|
||||
a: 3,
|
||||
},
|
||||
}
|
||||
s1.x.a = 11
|
||||
return s1.x.a
|
||||
}`,
|
||||
}
|
||||
`,
|
||||
big.NewInt(11),
|
||||
},
|
||||
{
|
||||
"complex struct default value",
|
||||
`package foo
|
||||
type S1 struct { x S2 }
|
||||
type S2 struct { y S3 }
|
||||
type S3 struct { a int }
|
||||
func Main() int {
|
||||
var s1 S1
|
||||
`type S5 struct { x S6 }
|
||||
type S6 struct { y S7 }
|
||||
type S7 struct { a int }
|
||||
func F%d() int {
|
||||
var s1 S5
|
||||
s1.x.y.a = 11
|
||||
return s1.x.y.a
|
||||
}`,
|
||||
}
|
||||
`,
|
||||
big.NewInt(11),
|
||||
},
|
||||
{
|
||||
"lengthy struct default value",
|
||||
`package foo
|
||||
type S struct { x int; y []byte; z bool }
|
||||
func Main() int {
|
||||
var s S
|
||||
`type SS struct { x int; y []byte; z bool }
|
||||
func F%d() int {
|
||||
var s SS
|
||||
return s.x
|
||||
}`,
|
||||
}
|
||||
`,
|
||||
big.NewInt(0),
|
||||
},
|
||||
{
|
||||
"nested selectors (complex write)",
|
||||
`package foo
|
||||
type S1 struct { x S2 }
|
||||
type S2 struct { y, z S3 }
|
||||
type S3 struct { a int }
|
||||
func Main() int {
|
||||
var s1 S1
|
||||
`type S8 struct { x S9 }
|
||||
type S9 struct { y, z S10 }
|
||||
type S10 struct { a int }
|
||||
func F%d() int {
|
||||
var s1 S8
|
||||
s1.x.y.a, s1.x.z.a = 11, 31
|
||||
return s1.x.y.a + s1.x.z.a
|
||||
}`,
|
||||
}
|
||||
`,
|
||||
big.NewInt(42),
|
||||
},
|
||||
{
|
||||
"omit field names",
|
||||
`package foo
|
||||
type pair struct { a, b int }
|
||||
func Main() int {
|
||||
`type pair struct { a, b int }
|
||||
func F%d() int {
|
||||
p := pair{1, 2}
|
||||
x := p.a * 10
|
||||
return x + p.b
|
||||
}`,
|
||||
}
|
||||
`,
|
||||
big.NewInt(12),
|
||||
},
|
||||
{
|
||||
"uninitialized struct fields",
|
||||
`package foo
|
||||
type Foo struct {
|
||||
`type Foo struct {
|
||||
i int
|
||||
m map[string]int
|
||||
b []byte
|
||||
|
@ -439,7 +418,7 @@ var structTestCases = []testCase{
|
|||
s struct { ii int }
|
||||
}
|
||||
func NewFoo() Foo { return Foo{} }
|
||||
func Main() int {
|
||||
func F%d() int {
|
||||
foo := NewFoo()
|
||||
if foo.i != 0 { return 1 }
|
||||
if len(foo.m) != 0 { return 1 }
|
||||
|
@ -448,11 +427,26 @@ var structTestCases = []testCase{
|
|||
s := foo.s
|
||||
if s.ii != 0 { return 1 }
|
||||
return 2
|
||||
}`,
|
||||
}
|
||||
`,
|
||||
big.NewInt(2),
|
||||
},
|
||||
}
|
||||
|
||||
func TestStructs(t *testing.T) {
|
||||
runTestCases(t, structTestCases)
|
||||
srcBuilder := bytes.NewBuffer([]byte("package testcase\n"))
|
||||
for i, tc := range structTestCases {
|
||||
srcBuilder.WriteString(fmt.Sprintf(tc.src, i))
|
||||
}
|
||||
|
||||
ne, di, err := compiler.CompileWithOptions("file.go", strings.NewReader(srcBuilder.String()), nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
for i, tc := range structTestCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
v := vm.New()
|
||||
invokeMethod(t, fmt.Sprintf("F%d", i), ne.Script, v, di)
|
||||
runAndCheck(t, v, tc.result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,28 +1,34 @@
|
|||
package compiler_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/compiler"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var switchTestCases = []testCase{
|
||||
{
|
||||
"simple switch success",
|
||||
`package main
|
||||
func Main() int {
|
||||
`func F%d() int {
|
||||
a := 5
|
||||
switch a {
|
||||
case 5: return 2
|
||||
}
|
||||
return 1
|
||||
}`,
|
||||
}
|
||||
`,
|
||||
big.NewInt(2),
|
||||
},
|
||||
{
|
||||
"switch with no tag",
|
||||
`package main
|
||||
func f() bool { return false }
|
||||
func Main() int {
|
||||
`func f() bool { return false }
|
||||
func F%d() int {
|
||||
switch {
|
||||
case f():
|
||||
return 1
|
||||
|
@ -30,14 +36,14 @@ var switchTestCases = []testCase{
|
|||
return 2
|
||||
}
|
||||
return 3
|
||||
}`,
|
||||
}
|
||||
`,
|
||||
big.NewInt(2),
|
||||
},
|
||||
{
|
||||
"type conversion in tag",
|
||||
`package main
|
||||
type state int
|
||||
func Main() int {
|
||||
`type state int
|
||||
func F%d() int {
|
||||
a := 1
|
||||
switch state(a) {
|
||||
case 1:
|
||||
|
@ -45,52 +51,52 @@ var switchTestCases = []testCase{
|
|||
default:
|
||||
return 11
|
||||
}
|
||||
}`,
|
||||
}
|
||||
`,
|
||||
big.NewInt(42),
|
||||
},
|
||||
{
|
||||
"simple switch fail",
|
||||
`package main
|
||||
func Main() int {
|
||||
`func F%d() int {
|
||||
a := 6
|
||||
switch a {
|
||||
case 5:
|
||||
return 2
|
||||
}
|
||||
return 1
|
||||
}`,
|
||||
}
|
||||
`,
|
||||
big.NewInt(1),
|
||||
},
|
||||
{
|
||||
"multiple cases success",
|
||||
`package main
|
||||
func Main() int {
|
||||
`func F%d() int {
|
||||
a := 6
|
||||
switch a {
|
||||
case 5: return 2
|
||||
case 6: return 3
|
||||
}
|
||||
return 1
|
||||
}`,
|
||||
}
|
||||
`,
|
||||
big.NewInt(3),
|
||||
},
|
||||
{
|
||||
"multiple cases fail",
|
||||
`package main
|
||||
func Main() int {
|
||||
`func F%d() int {
|
||||
a := 7
|
||||
switch a {
|
||||
case 5: return 2
|
||||
case 6: return 3
|
||||
}
|
||||
return 1
|
||||
}`,
|
||||
}
|
||||
`,
|
||||
big.NewInt(1),
|
||||
},
|
||||
{
|
||||
"default case",
|
||||
`package main
|
||||
func Main() int {
|
||||
`func F%d() int {
|
||||
a := 7
|
||||
switch a {
|
||||
case 5: return 2
|
||||
|
@ -98,13 +104,13 @@ var switchTestCases = []testCase{
|
|||
default: return 4
|
||||
}
|
||||
return 1
|
||||
}`,
|
||||
}
|
||||
`,
|
||||
big.NewInt(4),
|
||||
},
|
||||
{
|
||||
"empty case before default",
|
||||
`package main
|
||||
func Main() int {
|
||||
`func F%d() int {
|
||||
a := 6
|
||||
switch a {
|
||||
case 5: return 2
|
||||
|
@ -112,13 +118,13 @@ var switchTestCases = []testCase{
|
|||
default: return 4
|
||||
}
|
||||
return 1
|
||||
}`,
|
||||
}
|
||||
`,
|
||||
big.NewInt(1),
|
||||
},
|
||||
{
|
||||
"expression in case clause",
|
||||
`package main
|
||||
func Main() int {
|
||||
`func F%d() int {
|
||||
a := 6
|
||||
b := 3
|
||||
switch a {
|
||||
|
@ -126,13 +132,13 @@ var switchTestCases = []testCase{
|
|||
case b*3-3: return 3
|
||||
}
|
||||
return 1
|
||||
}`,
|
||||
}
|
||||
`,
|
||||
big.NewInt(3),
|
||||
},
|
||||
{
|
||||
"multiple expressions in case",
|
||||
`package main
|
||||
func Main() int {
|
||||
`func F%d() int {
|
||||
a := 8
|
||||
b := 3
|
||||
switch a {
|
||||
|
@ -140,13 +146,13 @@ var switchTestCases = []testCase{
|
|||
case b*3-3, 7, 8: return 3
|
||||
}
|
||||
return 1
|
||||
}`,
|
||||
}
|
||||
`,
|
||||
big.NewInt(3),
|
||||
},
|
||||
{
|
||||
"string switch",
|
||||
`package main
|
||||
func Main() int {
|
||||
`func F%d() int {
|
||||
name := "Valera"
|
||||
switch name {
|
||||
case "Misha": return 2
|
||||
|
@ -154,13 +160,13 @@ var switchTestCases = []testCase{
|
|||
case "Lera", "Valer" + "a": return 4
|
||||
}
|
||||
return 1
|
||||
}`,
|
||||
}
|
||||
`,
|
||||
big.NewInt(4),
|
||||
},
|
||||
{
|
||||
"break from switch",
|
||||
`package main
|
||||
func Main() int {
|
||||
`func F%d() int {
|
||||
i := 3
|
||||
switch i {
|
||||
case 2: return 2
|
||||
|
@ -171,13 +177,13 @@ var switchTestCases = []testCase{
|
|||
case 4: return 4
|
||||
}
|
||||
return i
|
||||
}`,
|
||||
}
|
||||
`,
|
||||
big.NewInt(1),
|
||||
},
|
||||
{
|
||||
"break from outer for",
|
||||
`package main
|
||||
func Main() int {
|
||||
`func F%d() int {
|
||||
i := 3
|
||||
loop:
|
||||
for i < 10 {
|
||||
|
@ -191,13 +197,13 @@ var switchTestCases = []testCase{
|
|||
}
|
||||
}
|
||||
return i
|
||||
}`,
|
||||
}
|
||||
`,
|
||||
big.NewInt(7),
|
||||
},
|
||||
{
|
||||
"continue outer for",
|
||||
`package main
|
||||
func Main() int {
|
||||
`func F%d() int {
|
||||
i := 2
|
||||
for i < 10 {
|
||||
i++
|
||||
|
@ -214,13 +220,13 @@ var switchTestCases = []testCase{
|
|||
}
|
||||
}
|
||||
return i
|
||||
}`,
|
||||
}
|
||||
`,
|
||||
big.NewInt(2),
|
||||
},
|
||||
{
|
||||
"simple fallthrough",
|
||||
`package main
|
||||
func Main() int {
|
||||
`func F%d() int {
|
||||
n := 2
|
||||
switch n {
|
||||
case 1: return 5
|
||||
|
@ -228,13 +234,13 @@ var switchTestCases = []testCase{
|
|||
case 3: return 6
|
||||
}
|
||||
return 7
|
||||
}`,
|
||||
}
|
||||
`,
|
||||
big.NewInt(6),
|
||||
},
|
||||
{
|
||||
"double fallthrough",
|
||||
`package main
|
||||
func Main() int {
|
||||
`func F%d() int {
|
||||
n := 2
|
||||
k := 5
|
||||
switch n {
|
||||
|
@ -249,15 +255,26 @@ var switchTestCases = []testCase{
|
|||
return k
|
||||
}
|
||||
return k
|
||||
}`,
|
||||
}
|
||||
`,
|
||||
big.NewInt(6),
|
||||
},
|
||||
}
|
||||
|
||||
func TestSwitch(t *testing.T) {
|
||||
for _, tc := range switchTestCases {
|
||||
srcBuilder := bytes.NewBuffer([]byte("package testcase\n"))
|
||||
for i, tc := range switchTestCases {
|
||||
srcBuilder.WriteString(fmt.Sprintf(tc.src, i))
|
||||
}
|
||||
|
||||
ne, di, err := compiler.CompileWithOptions("file.go", strings.NewReader(srcBuilder.String()), nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
for i, tc := range switchTestCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
eval(t, tc.src, tc.result)
|
||||
v := vm.New()
|
||||
invokeMethod(t, fmt.Sprintf("F%d", i), ne.Script, v, di)
|
||||
runAndCheck(t, v, tc.result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package compiler_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
@ -99,14 +100,41 @@ func TestSyscallExecution(t *testing.T) {
|
|||
// It will be set in test and we want to fail if calling invalid syscall.
|
||||
fs.Func = nil
|
||||
}
|
||||
|
||||
srcBuilder := bytes.NewBuffer(nil)
|
||||
srcBuilder.WriteString(`package foo
|
||||
import "github.com/nspcc-dev/neo-go/pkg/interop/contract"
|
||||
import "github.com/nspcc-dev/neo-go/pkg/interop/runtime"
|
||||
import "github.com/nspcc-dev/neo-go/pkg/interop/storage"
|
||||
import "github.com/nspcc-dev/neo-go/pkg/interop/iterator"
|
||||
import "github.com/nspcc-dev/neo-go/pkg/interop/crypto"
|
||||
import "github.com/nspcc-dev/neo-go/pkg/interop"
|
||||
func unused() { var _ interop.Hash160 }
|
||||
`)
|
||||
for goName, tc := range interops {
|
||||
realName := strings.ToTitle(strings.Replace(goName, ".", "", 1))
|
||||
var tmpl string
|
||||
if tc.isVoid {
|
||||
tmpl = "func %s() { %s(%s) }\n"
|
||||
} else {
|
||||
tmpl = "func %s() interface{} { return %s(%s) }\n"
|
||||
}
|
||||
srcBuilder.WriteString(fmt.Sprintf(tmpl, realName, goName, strings.Join(tc.params, ", ")))
|
||||
}
|
||||
|
||||
nf, di, err := compiler.CompileWithOptions("foo.go", srcBuilder, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
for goName, tc := range interops {
|
||||
t.Run(goName, func(t *testing.T) {
|
||||
runSyscallTestCase(t, ic, goName, tc)
|
||||
name := strings.ToTitle(strings.Replace(goName, ".", "", 1))
|
||||
runSyscallTestCase(t, ic, name, nf.Script, di, tc)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func runSyscallTestCase(t *testing.T, ic *interop.Context, goName string, tc syscallTestCase) {
|
||||
func runSyscallTestCase(t *testing.T, ic *interop.Context, realName string,
|
||||
script []byte, debugInfo *compiler.DebugInfo, tc syscallTestCase) {
|
||||
syscallID := interopnames.ToID([]byte(tc.method))
|
||||
f := ic.GetFunction(syscallID)
|
||||
require.NotNil(t, f)
|
||||
|
@ -127,30 +155,14 @@ func runSyscallTestCase(t *testing.T, ic *interop.Context, goName string, tc sys
|
|||
}
|
||||
defer func() { f.Func = nil }()
|
||||
|
||||
srcTmpl := `package foo
|
||||
import "github.com/nspcc-dev/neo-go/pkg/interop/%s"
|
||||
import "github.com/nspcc-dev/neo-go/pkg/interop"
|
||||
func unused() { var _ interop.Hash160 }
|
||||
`
|
||||
if tc.isVoid {
|
||||
srcTmpl += `func Main() { %s(%s) }`
|
||||
} else {
|
||||
srcTmpl += `func Main() interface{} { return %s(%s) }`
|
||||
}
|
||||
ss := strings.Split(goName, ".")
|
||||
src := fmt.Sprintf(srcTmpl, ss[0], goName, strings.Join(tc.params, ", "))
|
||||
b, _, err := compiler.CompileWithOptions("foo.go", strings.NewReader(src), nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
v := ic.SpawnVM()
|
||||
v.LoadScriptWithFlags(b.Script, callflag.All)
|
||||
require.NoError(t, v.Run())
|
||||
invokeMethod(t, realName, script, ic.VM, debugInfo)
|
||||
require.NoError(t, ic.VM.Run())
|
||||
require.True(t, called)
|
||||
if tc.isVoid {
|
||||
require.Equal(t, 0, v.Estack().Len())
|
||||
require.Equal(t, 0, ic.VM.Estack().Len())
|
||||
} else {
|
||||
require.Equal(t, 1, v.Estack().Len())
|
||||
require.Equal(t, big.NewInt(42), v.Estack().Pop().Value())
|
||||
require.Equal(t, 1, ic.VM.Estack().Len())
|
||||
require.Equal(t, big.NewInt(42), ic.VM.Estack().Pop().Value())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue