Merge pull request #2382 from nspcc-dev/compiler-optimize

compiler: optimize tests
This commit is contained in:
Roman Khimov 2022-03-04 18:55:40 +03:00 committed by GitHub
commit 96cd415384
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 804 additions and 783 deletions

View file

@ -1,16 +1,21 @@
package compiler_test package compiler_test
import ( import (
"bytes"
"fmt"
"math/big" "math/big"
"strings"
"testing" "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{ var assignTestCases = []testCase{
{ {
"chain define", "chain define",
` `func F%d() int {
package foo
func Main() int {
x := 4 x := 4
y := x y := x
z := y z := y
@ -23,9 +28,7 @@ var assignTestCases = []testCase{
}, },
{ {
"simple assign", "simple assign",
` `func F%d() int {
package foo
func Main() int {
x := 4 x := 4
x = 8 x = 8
return x return x
@ -35,9 +38,7 @@ var assignTestCases = []testCase{
}, },
{ {
"add assign", "add assign",
` `func F%d() int {
package foo
func Main() int {
x := 4 x := 4
x += 8 x += 8
return x return x
@ -47,9 +48,7 @@ var assignTestCases = []testCase{
}, },
{ {
"sub assign", "sub assign",
` `func F%d() int {
package foo
func Main() int {
x := 4 x := 4
x -= 2 x -= 2
return x return x
@ -59,9 +58,7 @@ var assignTestCases = []testCase{
}, },
{ {
"mul assign", "mul assign",
` `func F%d() int {
package foo
func Main() int {
x := 4 x := 4
x *= 2 x *= 2
return x return x
@ -71,9 +68,7 @@ var assignTestCases = []testCase{
}, },
{ {
"div assign", "div assign",
` `func F%d() int {
package foo
func Main() int {
x := 4 x := 4
x /= 2 x /= 2
return x return x
@ -83,9 +78,7 @@ var assignTestCases = []testCase{
}, },
{ {
"add assign binary expr", "add assign binary expr",
` `func F%d() int {
package foo
func Main() int {
x := 4 x := 4
x += 6 + 2 x += 6 + 2
return x return x
@ -95,9 +88,7 @@ var assignTestCases = []testCase{
}, },
{ {
"add assign binary expr ident", "add assign binary expr ident",
` `func F%d() int {
package foo
func Main() int {
x := 4 x := 4
y := 5 y := 5
x += 6 + y x += 6 + y
@ -108,19 +99,17 @@ var assignTestCases = []testCase{
}, },
{ {
"add assign for string", "add assign for string",
`package foo `func F%d() string {
func Main() string {
s := "Hello, " s := "Hello, "
s += "world!" s += "world!"
return s return s
}`, }
`,
[]byte("Hello, world!"), []byte("Hello, world!"),
}, },
{ {
"decl assign", "decl assign",
` `func F%d() int {
package foo
func Main() int {
var x int = 4 var x int = 4
return x return x
} }
@ -129,32 +118,41 @@ var assignTestCases = []testCase{
}, },
{ {
"multi assign", "multi assign",
` `func F%d() int {
package foo
func Main() int {
x, y := 1, 2 x, y := 1, 2
return x + y return x + y
} }
`, `,
big.NewInt(3), 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) { func TestAssignments(t *testing.T) {
runTestCases(t, assignTestCases) srcBuilder := bytes.NewBuffer([]byte("package testcase\n"))
} for i, tc := range assignTestCases {
srcBuilder.WriteString(fmt.Sprintf(tc.src, i))
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"
} }
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)
})
}
} }

View file

@ -15,9 +15,7 @@ import (
var binaryExprTestCases = []testCase{ var binaryExprTestCases = []testCase{
{ {
"simple add", "simple add",
` `func F%d() int {
package testcase
func Main() int {
x := 2 + 2 x := 2 + 2
return x return x
} }
@ -26,9 +24,7 @@ var binaryExprTestCases = []testCase{
}, },
{ {
"simple sub", "simple sub",
` `func F%d() int {
package testcase
func Main() int {
x := 2 - 2 x := 2 - 2
return x return x
} }
@ -37,9 +33,7 @@ var binaryExprTestCases = []testCase{
}, },
{ {
"simple div", "simple div",
` `func F%d() int {
package testcase
func Main() int {
x := 2 / 2 x := 2 / 2
return x return x
} }
@ -48,10 +42,8 @@ var binaryExprTestCases = []testCase{
}, },
{ {
"simple mod", "simple mod",
` `func F%d() int {
package testcase x := 3 %% 2
func Main() int {
x := 3 % 2
return x return x
} }
`, `,
@ -59,9 +51,7 @@ var binaryExprTestCases = []testCase{
}, },
{ {
"simple mul", "simple mul",
` `func F%d() int {
package testcase
func Main() int {
x := 4 * 2 x := 4 * 2
return x return x
} }
@ -70,9 +60,7 @@ var binaryExprTestCases = []testCase{
}, },
{ {
"simple binary expr in return", "simple binary expr in return",
` `func F%d() int {
package testcase
func Main() int {
x := 2 x := 2
return 2 + x return 2 + x
} }
@ -81,9 +69,7 @@ var binaryExprTestCases = []testCase{
}, },
{ {
"complex binary expr", "complex binary expr",
` `func F%d() int {
package testcase
func Main() int {
x := 4 x := 4
y := 8 y := 8
z := x + 2 + 2 - 8 z := x + 2 + 2 - 8
@ -94,9 +80,7 @@ var binaryExprTestCases = []testCase{
}, },
{ {
"compare not equal strings with eql", "compare not equal strings with eql",
` `func F%d() int {
package testcase
func Main() int {
str := "a string" str := "a string"
if str == "another string" { if str == "another string" {
return 1 return 1
@ -108,9 +92,7 @@ var binaryExprTestCases = []testCase{
}, },
{ {
"compare equal strings with eql", "compare equal strings with eql",
` `func F%d() int {
package testcase
func Main() int {
str := "a string" str := "a string"
if str == "a string" { if str == "a string" {
return 1 return 1
@ -122,9 +104,7 @@ var binaryExprTestCases = []testCase{
}, },
{ {
"compare not equal strings with neq", "compare not equal strings with neq",
` `func F%d() int {
package testcase
func Main() int {
str := "a string" str := "a string"
if str != "another string" { if str != "another string" {
return 1 return 1
@ -136,9 +116,7 @@ var binaryExprTestCases = []testCase{
}, },
{ {
"compare equal strings with neq", "compare equal strings with neq",
` `func F%d() int {
package testcase
func Main() int {
str := "a string" str := "a string"
if str != "a string" { if str != "a string" {
return 1 return 1
@ -150,9 +128,7 @@ var binaryExprTestCases = []testCase{
}, },
{ {
"compare equal ints with eql", "compare equal ints with eql",
` `func F%d() int {
package testcase
func Main() int {
x := 10 x := 10
if x == 10 { if x == 10 {
return 1 return 1
@ -164,9 +140,7 @@ var binaryExprTestCases = []testCase{
}, },
{ {
"compare equal ints with neq", "compare equal ints with neq",
` `func F%d() int {
package testcase
func Main() int {
x := 10 x := 10
if x != 10 { if x != 10 {
return 1 return 1
@ -178,9 +152,7 @@ var binaryExprTestCases = []testCase{
}, },
{ {
"compare not equal ints with eql", "compare not equal ints with eql",
` `func F%d() int {
package testcase
func Main() int {
x := 11 x := 11
if x == 10 { if x == 10 {
return 1 return 1
@ -192,9 +164,7 @@ var binaryExprTestCases = []testCase{
}, },
{ {
"compare not equal ints with neq", "compare not equal ints with neq",
` `func F%d() int {
package testcase
func Main() int {
x := 11 x := 11
if x != 10 { if x != 10 {
return 1 return 1
@ -206,9 +176,7 @@ var binaryExprTestCases = []testCase{
}, },
{ {
"simple add and assign", "simple add and assign",
` `func F%d() int {
package testcase
func Main() int {
x := 2 x := 2
x += 1 x += 1
return x return x
@ -218,9 +186,7 @@ var binaryExprTestCases = []testCase{
}, },
{ {
"simple sub and assign", "simple sub and assign",
` `func F%d() int {
package testcase
func Main() int {
x := 2 x := 2
x -= 1 x -= 1
return x return x
@ -230,9 +196,7 @@ var binaryExprTestCases = []testCase{
}, },
{ {
"simple mul and assign", "simple mul and assign",
` `func F%d() int {
package testcase
func Main() int {
x := 2 x := 2
x *= 2 x *= 2
return x return x
@ -242,9 +206,7 @@ var binaryExprTestCases = []testCase{
}, },
{ {
"simple div and assign", "simple div and assign",
` `func F%d() int {
package testcase
func Main() int {
x := 2 x := 2
x /= 2 x /= 2
return x return x
@ -254,11 +216,9 @@ var binaryExprTestCases = []testCase{
}, },
{ {
"simple mod and assign", "simple mod and assign",
` `func F%d() int {
package testcase
func Main() int {
x := 5 x := 5
x %= 2 x %%= 2
return x return x
} }
`, `,
@ -267,7 +227,23 @@ var binaryExprTestCases = []testCase{
} }
func TestBinaryExprs(t *testing.T) { 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 { 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) { 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 { for start := 0; start < len(triple); start += step {
testCases = testCases[:0] testCases = testCases[:0]
srcBuilder.Reset() srcBuilder.Reset()

View file

@ -1,9 +1,15 @@
package compiler_test package compiler_test
import ( import (
"bytes"
"fmt" "fmt"
"math/big" "math/big"
"strings"
"testing" "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 { type convertTestCase struct {
@ -25,12 +31,11 @@ func getFunctionName(typ string) string {
} }
func TestConvert(t *testing.T) { func TestConvert(t *testing.T) {
srcTmpl := `package foo srcTmpl := `func F%d() %s {
import "github.com/nspcc-dev/neo-go/pkg/interop/convert"
func Main() %s {
arg := %s arg := %s
return convert.To%s(arg) return convert.To%s(arg)
}` }
`
convertTestCases := []convertTestCase{ convertTestCases := []convertTestCase{
{"bool", "true", true}, {"bool", "true", true},
@ -53,11 +58,24 @@ func TestConvert(t *testing.T) {
{"[]byte", "[]byte{0, 1, 0}", []byte{0, 1, 0}}, {"[]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) name := getFunctionName(tc.returnType)
t.Run(tc.argValue+"->"+name, func(t *testing.T) { srcBuilder.WriteString(fmt.Sprintf(srcTmpl, i, tc.returnType, tc.argValue, name))
src := fmt.Sprintf(srcTmpl, tc.returnType, tc.argValue, name) }
eval(t, src, tc.result)
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)
}) })
} }
} }

View file

@ -1,11 +1,16 @@
package compiler_test package compiler_test
import ( import (
"bytes"
"fmt" "fmt"
"math/big" "math/big"
"strings"
"testing" "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/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/stretchr/testify/require"
) )
func TestEntryPointWithMethod(t *testing.T) { func TestEntryPointWithMethod(t *testing.T) {
@ -370,405 +375,368 @@ func TestDec(t *testing.T) {
eval(t, src, big.NewInt(1)) eval(t, src, big.NewInt(1))
} }
func TestForLoopEmpty(t *testing.T) { var forLoopTestCases = []testCase{
src := ` {
package foo "empty for loop",
func Main() int { `func F%d() int {
x := 0 x := 0
for { for {
x++ x++
if x == 2 { if x == 2 { break }
break
} }
return x
} }
return x `,
} big.NewInt(2),
` }, {
eval(t, src, big.NewInt(2)) "big iteration count",
} `func F%d() int {
func TestForLoopBigIter(t *testing.T) {
src := `
package foo
func Main() int {
x := 0 x := 0
for i := 0; i < 100000; i++ { for i := 0; i < 100000; i++ {
x = i x = i
} }
return x return x
} }
` `,
eval(t, src, big.NewInt(99999)) big.NewInt(99999),
} },
{
func TestForLoopNoInit(t *testing.T) { "no init",
src := ` `func F%d() int {
package foo
func Main() int {
i := 0 i := 0
for ; i < 10; i++ { for ; i < 10; i++ {
} }
return i return i
} }
` `,
eval(t, src, big.NewInt(10)) big.NewInt(10),
} },
{
func TestForLoopNoPost(t *testing.T) { "no post",
src := ` `func F%d() int {
package foo
func Main() int {
i := 0 i := 0
for i < 10 { for i < 10 {
i++ i++
} }
return i return i
} }
` `,
eval(t, src, big.NewInt(10)) big.NewInt(10),
} },
{
func TestForLoopRange(t *testing.T) { "range",
src := ` `func F%d() int {
package foo sum := 0
func Main() int { arr := []int{1, 2, 3}
sum := 0 for i := range arr {
arr := []int{1, 2, 3} sum += arr[i]
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
} }
return sum
} }
return i `,
}` big.NewInt(6),
},
eval(t, src, big.NewInt(5)) {
} "range, global index",
`func F%d() int {
func TestForLoopBreakLabel(t *testing.T) { sum := 0
src := ` i := 0
package foo arr := []int{1, 2, 3}
func Main() int { for i = range arr {
var i int sum += arr[i]
loop:
for i < 10 {
i++
if i == 5 {
break loop
} }
return sum + i
} }
return i `,
}` big.NewInt(8),
},
eval(t, src, big.NewInt(5)) {
} "range, change variable",
`func F%d() int {
func TestForLoopNestedBreak(t *testing.T) { sum := 0
src := ` arr := []int{1, 2, 3}
package foo for i := range arr {
func Main() int { sum += arr[i]
var i int i++
for i < 10 { sum += i
i++ }
for j := 0; j < 2; j++ { return sum
}
`,
big.NewInt(12),
},
{
"break",
`func F%d() int {
var i int
for i < 10 {
i++ i++
if i == 5 { if i == 5 {
break break
} }
} }
return i
} }
return i `,
}` big.NewInt(5),
},
eval(t, src, big.NewInt(11)) {
} "break label",
`func F%d() int {
func TestForLoopNestedBreakLabel(t *testing.T) { var i int
src := ` loop:
package foo for i < 10 {
func Main() int { i++
var i int
loop:
for i < 10 {
i++
for j := 0; j < 2; j++ {
if i == 5 { if i == 5 {
break loop break loop
} }
}
return i
}
`,
big.NewInt(5),
},
{
"nested break",
`func F%d() int {
var i int
for i < 10 {
i++ i++
for j := 0; j < 2; j++ {
i++
if i == 5 {
break
}
}
} }
return i
} }
return i `,
}` big.NewInt(11),
},
eval(t, src, big.NewInt(5)) {
} "nested break label",
`func F%d() int {
func TestForLoopContinue(t *testing.T) { var i int
src := ` loop:
package foo for i < 10 {
func Main() int { i++
var i, j int for j := 0; j < 2; j++ {
for i < 10 { if i == 5 {
i++ break loop
if i >= 5 { }
continue i++
}
} }
j++ return i
} }
return j `,
}` big.NewInt(5),
},
eval(t, src, big.NewInt(4)) {
} "continue",
`func F%d() int {
func TestForLoopContinueLabel(t *testing.T) { var i, j int
src := ` for i < 10 {
package foo i++
func Main() int { if i >= 5 {
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 {
continue continue
} }
k++ j++
} }
return j
} }
return k `,
}` big.NewInt(4),
},
eval(t, src, big.NewInt(20)) {
} "continue label",
`func F%d() int {
func TestForLoopNestedContinueLabel(t *testing.T) { var i, j int
src := ` loop:
package foo for i < 10 {
func Main() int { i++
var i int if i >= 5 {
loop:
for ; i < 10; i += 10 {
i++
for j := 0; j < 4; j++ {
if i == 5 {
continue loop 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++ i++
} }
return i
} }
return i `,
}` big.NewInt(8),
},
eval(t, src, big.NewInt(15)) {
} "range, no variable",
`func F%d() int {
func TestForLoopRangeBreak(t *testing.T) { sum := 0
src := ` arr := []int{1, 2, 3}
package foo for range arr {
func Main() int { sum += 1
var i int
arr := []int{1, 2, 3}
for i = range arr {
if arr[i] == 2 {
break
} }
return sum
} }
return i `,
}` big.NewInt(3),
},
eval(t, src, big.NewInt(1)) {
} "range value",
`func f(a int) int { return a }
func TestForLoopRangeNestedBreak(t *testing.T) { func F%d() int {
src := ` var sum int
package foo arr := []int{1, 9, 4}
func Main() int { for _, v := range arr {
k := 5 sum += f(v)
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 sum
} }
return k `,
}` big.NewInt(14),
},
eval(t, src, big.NewInt(10)) {
} "range, map",
`func F%d() int {
func TestForLoopRangeContinue(t *testing.T) { m := map[int]int{
src := ` 1: 13,
package foo 11: 17,
func Main() int {
i := 6
arr := []int{1, 2, 3}
for j := range arr {
if arr[j] < 2 {
continue
} }
i++ var sum int
for i, v := range m {
sum += i
sum += v
}
return sum
} }
return i `,
}` big.NewInt(42),
},
eval(t, src, big.NewInt(8)) {
"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) { func TestForLoop(t *testing.T) {
src := ` srcBuilder := bytes.NewBuffer([]byte("package testcase\n"))
package foo for i, tc := range forLoopTestCases {
func Main() int { srcBuilder.WriteString(fmt.Sprintf(tc.src, i))
sum := 0 }
arr := []int{1, 2, 3}
for range arr {
sum += 1
}
return sum
}`
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) { for i, tc := range forLoopTestCases {
src := ` v := vm.New()
package foo t.Run(tc.name, func(t *testing.T) {
func f(a int) int { return a } v.Istack().Clear()
func Main() int { v.Estack().Clear()
var sum int invokeMethod(t, fmt.Sprintf("F%d", i), ne.Script, v, di)
arr := []int{1, 9, 4} runAndCheck(t, v, tc.result)
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))
} }
func TestForLoopComplexConditions(t *testing.T) { func TestForLoopComplexConditions(t *testing.T) {
src := ` forCondTestCases := []struct {
package foo
func Main() int {
var ok bool
_ = ok
i := 0
j := 0
%s
for %s {
i++
j++
%s
}
return i
}`
tests := []struct {
Name string Name string
Cond string Cond string
Assign 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}, {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 name := tc.Cond
if tc.Assign != "" { if tc.Assign != "" {
name = tc.Assign name = tc.Assign
} }
t.Run(name, func(t *testing.T) { t.Run(name, func(t *testing.T) {
s := fmt.Sprintf(src, tc.Assign, tc.Cond, tc.Assign) v.Istack().Clear()
eval(t, s, big.NewInt(tc.Result)) v.Estack().Clear()
invokeMethod(t, fmt.Sprintf("F%d", i), ne.Script, v, di)
runAndCheck(t, v, big.NewInt(tc.Result))
}) })
} }
} }

View file

@ -1,9 +1,11 @@
package compiler_test package compiler_test
import ( import (
"bytes"
"errors" "errors"
"fmt" "fmt"
"math/big" "math/big"
"strconv"
"strings" "strings"
"testing" "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/roles"
"github.com/nspcc-dev/neo-go/pkg/interop/native/std" "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"
"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"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/stretchr/testify/require" "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) { 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) { 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 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) md := getMethod(t, ctr, method, params)
isVoid := md.MD.ReturnType == smartcontract.VoidType isVoid := md.MD.ReturnType == smartcontract.VoidType
srcTmpl := `package foo srcBuilder.WriteString("func F" + strconv.Itoa(i) + "() ")
import "github.com/nspcc-dev/neo-go/pkg/interop/native/%s" if !isVoid {
import "github.com/nspcc-dev/neo-go/pkg/interop" srcBuilder.WriteString("interface{} { return ")
var _ interop.Hash160
`
if isVoid {
srcTmpl += `func Main() { %s.%s(%s) }`
} else { } else {
srcTmpl += `func Main() interface{} { return %s.%s(%s) }` srcBuilder.WriteString("{ ")
} }
methodUpper := strings.ToUpper(method[:1]) + method[1:] // ASCII only methodUpper := strings.ToUpper(method[:1]) + method[1:] // ASCII only
methodUpper = strings.ReplaceAll(methodUpper, "Gas", "GAS") methodUpper = strings.ReplaceAll(methodUpper, "Gas", "GAS")
methodUpper = strings.ReplaceAll(methodUpper, "Json", "JSON") 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 := 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 { v.LoadToken = func(id int32) error {
t := b.Tokens[id] t := b.Tokens[id]
if t.Hash != ctr.Hash { if t.Hash != ctr.Hash {
@ -285,7 +302,7 @@ func runNativeTestCase(t *testing.T, ctr interop.ContractMD, name, method string
} }
return nil 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()) require.NoError(t, v.Run())
if isVoid { if isVoid {
require.Equal(t, 0, v.Estack().Len()) require.Equal(t, 0, v.Estack().Len())

View file

@ -1,11 +1,14 @@
package compiler_test package compiler_test
import ( import (
"bytes"
"fmt"
"math/big" "math/big"
"strings" "strings"
"testing" "testing"
"github.com/nspcc-dev/neo-go/pkg/compiler" "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/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -13,9 +16,7 @@ import (
var sliceTestCases = []testCase{ var sliceTestCases = []testCase{
{ {
"constant index", "constant index",
` `func F%d() int {
package foo
func Main() int {
a := []int{0,0} a := []int{0,0}
a[1] = 42 a[1] = 42
return a[1]+0 return a[1]+0
@ -25,9 +26,7 @@ var sliceTestCases = []testCase{
}, },
{ {
"variable index", "variable index",
` `func F%d() int {
package foo
func Main() int {
a := []int{0,0} a := []int{0,0}
i := 1 i := 1
a[i] = 42 a[i] = 42
@ -38,19 +37,17 @@ var sliceTestCases = []testCase{
}, },
{ {
"increase slice element with +=", "increase slice element with +=",
`package foo `func F%d() int {
func Main() int {
a := []int{1, 2, 3} a := []int{1, 2, 3}
a[1] += 40 a[1] += 40
return a[1] return a[1]
}`, }
`,
big.NewInt(42), big.NewInt(42),
}, },
{ {
"complex test", "complex test",
` `func F%d() int {
package foo
func Main() int {
a := []int{1,2,3} a := []int{1,2,3}
x := a[0] x := a[0]
a[x] = a[x] + 4 a[x] = a[x] + 4
@ -62,9 +59,7 @@ var sliceTestCases = []testCase{
}, },
{ {
"slice literals with variables", "slice literals with variables",
` `func F%d() int {
package foo
func Main() int {
elem := 7 elem := 7
a := []int{6, elem, 8} a := []int{6, elem, 8}
return a[1] return a[1]
@ -74,9 +69,7 @@ var sliceTestCases = []testCase{
}, },
{ {
"slice literals with expressions", "slice literals with expressions",
` `func F%d() int {
package foo
func Main() int {
elem := []int{3, 7} elem := []int{3, 7}
a := []int{6, elem[1]*2+1, 24} a := []int{6, elem[1]*2+1, 24}
return a[1] return a[1]
@ -86,93 +79,88 @@ var sliceTestCases = []testCase{
}, },
{ {
"sub-slice with literal bounds", "sub-slice with literal bounds",
` `func F%d() []byte {
package foo
func Main() []byte {
a := []byte{0, 1, 2, 3} a := []byte{0, 1, 2, 3}
b := a[1:3] b := a[1:3]
return b return b
}`, }
`,
[]byte{1, 2}, []byte{1, 2},
}, },
{ {
"sub-slice with constant bounds", "sub-slice with constant bounds",
` `const x = 1
package foo
const x = 1
const y = 3 const y = 3
func Main() []byte { func F%d() []byte {
a := []byte{0, 1, 2, 3} a := []byte{0, 1, 2, 3}
b := a[x:y] b := a[x:y]
return b return b
}`, }
`,
[]byte{1, 2}, []byte{1, 2},
}, },
{ {
"sub-slice with variable bounds", "sub-slice with variable bounds",
` `func F%d() []byte {
package foo
func Main() []byte {
a := []byte{0, 1, 2, 3} a := []byte{0, 1, 2, 3}
x := 1 x := 1
y := 3 y := 3
b := a[x:y] b := a[x:y]
return b return b
}`, }
`,
[]byte{1, 2}, []byte{1, 2},
}, },
{ {
"sub-slice with no lower bound", "sub-slice with no lower bound",
` `func F%d() []byte {
package foo
func Main() []byte {
a := []byte{0, 1, 2, 3} a := []byte{0, 1, 2, 3}
b := a[:3] b := a[:3]
return b return b
}`, }
`,
[]byte{0, 1, 2}, []byte{0, 1, 2},
}, },
{ {
"sub-slice with no upper bound", "sub-slice with no upper bound",
` `func F%d() []byte {
package foo
func Main() []byte {
a := []byte{0, 1, 2, 3} a := []byte{0, 1, 2, 3}
b := a[2:] b := a[2:]
return b return b
}`, }
`,
[]byte{2, 3}, []byte{2, 3},
}, },
{ {
"declare byte slice", "declare byte slice",
`package foo `func F%d() []byte {
func Main() []byte {
var a []byte var a []byte
a = append(a, 1) a = append(a, 1)
a = append(a, 2) a = append(a, 2)
return a return a
}`, }
`,
[]byte{1, 2}, []byte{1, 2},
}, },
{ {
"append multiple bytes to a slice", "append multiple bytes to a slice",
`package foo `func F%d() []byte {
func Main() []byte {
var a []byte var a []byte
a = append(a, 1, 2) a = append(a, 1, 2)
return a return a
}`, }
`,
[]byte{1, 2}, []byte{1, 2},
}, },
{ {
"append multiple ints to a slice", "append multiple ints to a slice",
`package foo `func F%d() []int {
func Main() []int {
var a []int var a []int
a = append(a, 1, 2, 3) a = append(a, 1, 2, 3)
a = append(a, 4, 5) a = append(a, 4, 5)
return a return a
}`, }
`,
[]stackitem.Item{ []stackitem.Item{
stackitem.NewBigInteger(big.NewInt(1)), stackitem.NewBigInteger(big.NewInt(1)),
stackitem.NewBigInteger(big.NewInt(2)), stackitem.NewBigInteger(big.NewInt(2)),
@ -183,15 +171,15 @@ var sliceTestCases = []testCase{
}, },
{ {
"int slice, append slice", "int slice, append slice",
`package foo ` func getByte() byte { return 0x80 }
func getByte() byte { return 0x80 } func F%d() []int {
func Main() []int {
x := []int{1} x := []int{1}
y := []int{2, 3} y := []int{2, 3}
x = append(x, y...) x = append(x, y...)
x = append(x, y...) x = append(x, y...)
return x return x
}`, }
`,
[]stackitem.Item{ []stackitem.Item{
stackitem.Make(1), stackitem.Make(1),
stackitem.Make(2), stackitem.Make(3), stackitem.Make(2), stackitem.Make(3),
@ -200,13 +188,13 @@ var sliceTestCases = []testCase{
}, },
{ {
"declare compound slice", "declare compound slice",
`package foo `func F%d() []string {
func Main() []string {
var a []string var a []string
a = append(a, "a") a = append(a, "a")
a = append(a, "b") a = append(a, "b")
return a return a
}`, }
`,
[]stackitem.Item{ []stackitem.Item{
stackitem.NewByteArray([]byte("a")), stackitem.NewByteArray([]byte("a")),
stackitem.NewByteArray([]byte("b")), stackitem.NewByteArray([]byte("b")),
@ -214,14 +202,14 @@ var sliceTestCases = []testCase{
}, },
{ {
"declare compound slice alias", "declare compound slice alias",
`package foo `type strs []string
type strs []string func F%d() []string {
func Main() []string {
var a strs var a strs
a = append(a, "a") a = append(a, "a")
a = append(a, "b") a = append(a, "b")
return a return a
}`, }
`,
[]stackitem.Item{ []stackitem.Item{
stackitem.NewByteArray([]byte("a")), stackitem.NewByteArray([]byte("a")),
stackitem.NewByteArray([]byte("b")), stackitem.NewByteArray([]byte("b")),
@ -229,72 +217,70 @@ var sliceTestCases = []testCase{
}, },
{ {
"byte-slice assignment", "byte-slice assignment",
`package foo `func F%d() []byte {
func Main() []byte {
a := []byte{0, 1, 2} a := []byte{0, 1, 2}
a[1] = 42 a[1] = 42
return a return a
}`, }
`,
[]byte{0, 42, 2}, []byte{0, 42, 2},
}, },
{ {
"byte-slice assignment after string conversion", "byte-slice assignment after string conversion",
`package foo `func F%d() []byte {
func Main() []byte {
a := "abc" a := "abc"
b := []byte(a) b := []byte(a)
b[1] = 42 b[1] = 42
return []byte(a) return []byte(a)
}`, }
`,
[]byte{0x61, 0x62, 0x63}, []byte{0x61, 0x62, 0x63},
}, },
{ {
"declare and append byte-slice", "declare and append byte-slice",
`package foo `func F%d() []byte {
func Main() []byte {
var a []byte var a []byte
a = append(a, 1) a = append(a, 1)
a = append(a, 2) a = append(a, 2)
return a return a
}`, }
`,
[]byte{1, 2}, []byte{1, 2},
}, },
{ {
"nested slice assignment", "nested slice assignment",
`package foo `func F%d() int {
func Main() int {
a := [][]int{[]int{1, 2}, []int{3, 4}} a := [][]int{[]int{1, 2}, []int{3, 4}}
a[1][0] = 42 a[1][0] = 42
return a[1][0] return a[1][0]
}`, }
`,
big.NewInt(42), big.NewInt(42),
}, },
{ {
"nested slice omitted type (slice)", "nested slice omitted type (slice)",
`package foo `func F%d() int {
func Main() int {
a := [][]int{{1, 2}, {3, 4}} a := [][]int{{1, 2}, {3, 4}}
a[1][0] = 42 a[1][0] = 42
return a[1][0] return a[1][0]
}`, }
`,
big.NewInt(42), big.NewInt(42),
}, },
{ {
"nested slice omitted type (struct)", "nested slice omitted type (struct)",
`package foo `type pairA struct { a, b int }
type pair struct { a, b int } func F%d() int {
func Main() int { a := []pairA{{a: 1, b: 2}, {a: 3, b: 4}}
a := []pair{{a: 1, b: 2}, {a: 3, b: 4}}
a[1].a = 42 a[1].a = 42
return a[1].a return a[1].a
}`, }
`,
big.NewInt(42), big.NewInt(42),
}, },
{ {
"defaults to nil for byte slice", "defaults to nil for byte slice",
` `func F%d() int {
package foo
func Main() int {
var a []byte var a []byte
if a != nil { return 1} if a != nil { return 1}
return 2 return 2
@ -304,9 +290,7 @@ var sliceTestCases = []testCase{
}, },
{ {
"defaults to nil for int slice", "defaults to nil for int slice",
` `func F%d() int {
package foo
func Main() int {
var a []int var a []int
if a != nil { return 1} if a != nil { return 1}
return 2 return 2
@ -316,11 +300,9 @@ var sliceTestCases = []testCase{
}, },
{ {
"defaults to nil for struct slice", "defaults to nil for struct slice",
` `type pairB struct { a, b int }
package foo func F%d() int {
type pair struct { a, b int } var a []pairB
func Main() int {
var a []pair
if a != nil { return 1} if a != nil { return 1}
return 2 return 2
} }
@ -329,28 +311,42 @@ var sliceTestCases = []testCase{
}, },
{ {
"literal byte-slice with variable values", "literal byte-slice with variable values",
`package foo `const sym1 = 's'
const sym1 = 's' func F%d() []byte {
func Main() []byte {
sym2 := byte('t') sym2 := byte('t')
sym4 := byte('i') sym4 := byte('i')
return []byte{sym1, sym2, 'r', sym4, 'n', 'g'} return []byte{sym1, sym2, 'r', sym4, 'n', 'g'}
}`, }
`,
[]byte("string"), []byte("string"),
}, },
{ {
"literal slice with function call", "literal slice with function call",
`package foo `func fn() byte { return 't' }
func fn() byte { return 't' } func F%d() []byte {
func Main() []byte {
return []byte{'s', fn(), 'r'} return []byte{'s', fn(), 'r'}
}`, }
`,
[]byte("str"), []byte("str"),
}, },
} }
func TestSliceOperations(t *testing.T) { 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) { func TestByteSlices(t *testing.T) {

View file

@ -1,19 +1,23 @@
package compiler_test package compiler_test
import ( import (
"bytes"
"fmt"
"math/big" "math/big"
"strings"
"testing" "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/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/stretchr/testify/require"
) )
var structTestCases = []testCase{ var structTestCases = []testCase{
{ {
"struct field assign", "struct field assign",
` `func F%d() int {
package foo t := token1 {
func Main() int {
t := token {
x: 2, x: 2,
y: 4, y: 4,
} }
@ -22,7 +26,7 @@ var structTestCases = []testCase{
return age return age
} }
type token struct { type token1 struct {
x int x int
y int y int
} }
@ -31,11 +35,9 @@ var structTestCases = []testCase{
}, },
{ {
"struct field from func result", "struct field from func result",
` `type S struct { x int }
package foo
type S struct { x int }
func fn() int { return 2 } func fn() int { return 2 }
func Main() int { func F%d() int {
t := S{x: fn()} t := S{x: fn()}
return t.x return t.x
} }
@ -44,15 +46,13 @@ var structTestCases = []testCase{
}, },
{ {
"struct field return", "struct field return",
` `type token2 struct {
package foo
type token struct {
x int x int
y int y int
} }
func Main() int { func F%d() int {
t := token { t := token2 {
x: 2, x: 2,
y: 4, y: 4,
} }
@ -64,15 +64,13 @@ var structTestCases = []testCase{
}, },
{ {
"struct field assign", "struct field assign",
` `type token3 struct {
package foo
type token struct {
x int x int
y int y int
} }
func Main() int { func F%d() int {
t := token { t := token3 {
x: 2, x: 2,
y: 4, y: 4,
} }
@ -84,17 +82,15 @@ var structTestCases = []testCase{
}, },
{ {
"complex struct", "complex struct",
` `type token4 struct {
package foo
type token struct {
x int x int
y int y int
} }
func Main() int { func F%d() int {
x := 10 x := 10
t := token { t := token4 {
x: 2, x: 2,
y: 4, y: 4,
} }
@ -108,95 +104,90 @@ var structTestCases = []testCase{
}, },
{ {
"initialize struct field from variable", "initialize struct field from variable",
` `type token5 struct {
package foo
type token struct {
x int x int
y int y int
} }
func Main() int { func F%d() int {
x := 10 x := 10
t := token { t := token5 {
x: x, x: x,
y: 4, y: 4,
} }
y := t.x + t.y y := t.x + t.y
return y return y
}`, }
`,
big.NewInt(14), big.NewInt(14),
}, },
{ {
"assign a variable to a struct field", "assign a variable to a struct field",
` `type token6 struct {
package foo
type token struct {
x int x int
y int y int
} }
func Main() int { func F%d() int {
ten := 10 ten := 10
t := token { t := token6 {
x: 2, x: 2,
y: 4, y: 4,
} }
t.x = ten t.x = ten
y := t.y + t.x y := t.y + t.x
return y return y
}`, }
`,
big.NewInt(14), big.NewInt(14),
}, },
{ {
"increase struct field with +=", "increase struct field with +=",
`package foo `type token7 struct { x int }
type token struct { x int } func F%d() int {
func Main() int { t := token7{x: 2}
t := token{x: 2}
t.x += 3 t.x += 3
return t.x return t.x
}`, }
`,
big.NewInt(5), big.NewInt(5),
}, },
{ {
"assign a struct field to a struct field", "assign a struct field to a struct field",
` `type token8 struct {
package foo
type token struct {
x int x int
y int y int
} }
func Main() int { func F%d() int {
t1 := token { t1 := token8 {
x: 2, x: 2,
y: 4, y: 4,
} }
t2 := token { t2 := token8 {
x: 3, x: 3,
y: 5, y: 5,
} }
t1.x = t2.y t1.x = t2.y
y := t1.x + t2.x y := t1.x + t2.x
return y return y
}`, }
`,
big.NewInt(8), big.NewInt(8),
}, },
{ {
"initialize same struct twice", "initialize same struct twice",
` `type token9 struct {
package foo
type token struct {
x int x int
y int y int
} }
func Main() int { func F%d() int {
t1 := token { t1 := token9 {
x: 2, x: 2,
y: 4, y: 4,
} }
t2 := token { t2 := token9 {
x: 2, x: 2,
y: 4, y: 4,
} }
@ -207,18 +198,16 @@ var structTestCases = []testCase{
}, },
{ {
"struct methods", "struct methods",
` `type token10 struct {
package foo
type token struct {
x int x int
} }
func(t token) getInteger() int { func(t token10) getInteger() int {
return t.x return t.x
} }
func Main() int { func F%d() int {
t := token { t := token10 {
x: 4, x: 4,
} }
someInt := t.getInteger() someInt := t.getInteger()
@ -229,19 +218,17 @@ var structTestCases = []testCase{
}, },
{ {
"struct methods with arguments", "struct methods with arguments",
` `type token11 struct {
package foo
type token struct {
x int x int
} }
// Also tests if x conflicts with t.x // 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 return t.x + x + y
} }
func Main() int { func F%d() int {
t := token { t := token11 {
x: 4, x: 4,
} }
someInt := t.addIntegers(2, 4) someInt := t.addIntegers(2, 4)
@ -252,17 +239,15 @@ var structTestCases = []testCase{
}, },
{ {
"initialize struct partially", "initialize struct partially",
` `type token12 struct {
package foo
type token struct {
x int x int
y int y int
z string z string
b bool b bool
} }
func Main() int { func F%d() int {
t := token { t := token12 {
x: 4, x: 4,
} }
return t.y return t.y
@ -272,17 +257,15 @@ var structTestCases = []testCase{
}, },
{ {
"test return struct from func", "test return struct from func",
` `type token13 struct {
package foo
type token struct {
x int x int
y int y int
z string z string
b bool b bool
} }
func newToken() token { func newToken() token13 {
return token{ return token13{
x: 1, x: 1,
y: 2, y: 2,
z: "hello", z: "hello",
@ -290,7 +273,7 @@ var structTestCases = []testCase{
} }
} }
func Main() token { func F%d() token13 {
return newToken() return newToken()
} }
`, `,
@ -303,10 +286,7 @@ var structTestCases = []testCase{
}, },
{ {
"pass struct as argument", "pass struct as argument",
` `type Bar struct {
package foo
type Bar struct {
amount int amount int
} }
@ -315,7 +295,7 @@ var structTestCases = []testCase{
return bar.amount return bar.amount
} }
func Main() int { func F%d() int {
b := Bar{ b := Bar{
amount: 10, amount: 10,
} }
@ -328,110 +308,109 @@ var structTestCases = []testCase{
}, },
{ {
"declare struct literal", "declare struct literal",
`package foo `func F%d() int {
func Main() int {
var x struct { var x struct {
a int a int
} }
x.a = 2 x.a = 2
return x.a return x.a
}`, }
`,
big.NewInt(2), big.NewInt(2),
}, },
{ {
"declare struct type", "declare struct type",
`package foo `type withA struct {
type withA struct {
a int a int
} }
func Main() int { func F%d() int {
var x withA var x withA
x.a = 2 x.a = 2
return x.a return x.a
}`, }
`,
big.NewInt(2), big.NewInt(2),
}, },
{ {
"nested selectors (simple read)", "nested selectors (simple read)",
`package foo `type S1 struct { x, y S2 }
type S1 struct { x, y S2 }
type S2 struct { a, b int } type S2 struct { a, b int }
func Main() int { func F%d() int {
var s1 S1 var s1 S1
var s2 S2 var s2 S2
s2.a = 3 s2.a = 3
s1.y = s2 s1.y = s2
return s1.y.a return s1.y.a
}`, }
`,
big.NewInt(3), big.NewInt(3),
}, },
{ {
"nested selectors (simple write)", "nested selectors (simple write)",
`package foo `type S3 struct { x S4 }
type S1 struct { x S2 } type S4 struct { a int }
type S2 struct { a int } func F%d() int {
func Main() int { s1 := S3{
s1 := S1{ x: S4 {
x: S2 {
a: 3, a: 3,
}, },
} }
s1.x.a = 11 s1.x.a = 11
return s1.x.a return s1.x.a
}`, }
`,
big.NewInt(11), big.NewInt(11),
}, },
{ {
"complex struct default value", "complex struct default value",
`package foo `type S5 struct { x S6 }
type S1 struct { x S2 } type S6 struct { y S7 }
type S2 struct { y S3 } type S7 struct { a int }
type S3 struct { a int } func F%d() int {
func Main() int { var s1 S5
var s1 S1
s1.x.y.a = 11 s1.x.y.a = 11
return s1.x.y.a return s1.x.y.a
}`, }
`,
big.NewInt(11), big.NewInt(11),
}, },
{ {
"lengthy struct default value", "lengthy struct default value",
`package foo `type SS struct { x int; y []byte; z bool }
type S struct { x int; y []byte; z bool } func F%d() int {
func Main() int { var s SS
var s S
return s.x return s.x
}`, }
`,
big.NewInt(0), big.NewInt(0),
}, },
{ {
"nested selectors (complex write)", "nested selectors (complex write)",
`package foo `type S8 struct { x S9 }
type S1 struct { x S2 } type S9 struct { y, z S10 }
type S2 struct { y, z S3 } type S10 struct { a int }
type S3 struct { a int } func F%d() int {
func Main() int { var s1 S8
var s1 S1
s1.x.y.a, s1.x.z.a = 11, 31 s1.x.y.a, s1.x.z.a = 11, 31
return s1.x.y.a + s1.x.z.a return s1.x.y.a + s1.x.z.a
}`, }
`,
big.NewInt(42), big.NewInt(42),
}, },
{ {
"omit field names", "omit field names",
`package foo `type pair struct { a, b int }
type pair struct { a, b int } func F%d() int {
func Main() int {
p := pair{1, 2} p := pair{1, 2}
x := p.a * 10 x := p.a * 10
return x + p.b return x + p.b
}`, }
`,
big.NewInt(12), big.NewInt(12),
}, },
{ {
"uninitialized struct fields", "uninitialized struct fields",
`package foo `type Foo struct {
type Foo struct {
i int i int
m map[string]int m map[string]int
b []byte b []byte
@ -439,7 +418,7 @@ var structTestCases = []testCase{
s struct { ii int } s struct { ii int }
} }
func NewFoo() Foo { return Foo{} } func NewFoo() Foo { return Foo{} }
func Main() int { func F%d() int {
foo := NewFoo() foo := NewFoo()
if foo.i != 0 { return 1 } if foo.i != 0 { return 1 }
if len(foo.m) != 0 { return 1 } if len(foo.m) != 0 { return 1 }
@ -448,11 +427,26 @@ var structTestCases = []testCase{
s := foo.s s := foo.s
if s.ii != 0 { return 1 } if s.ii != 0 { return 1 }
return 2 return 2
}`, }
`,
big.NewInt(2), big.NewInt(2),
}, },
} }
func TestStructs(t *testing.T) { 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)
})
}
} }

View file

@ -1,28 +1,34 @@
package compiler_test package compiler_test
import ( import (
"bytes"
"fmt"
"math/big" "math/big"
"strings"
"testing" "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{ var switchTestCases = []testCase{
{ {
"simple switch success", "simple switch success",
`package main `func F%d() int {
func Main() int {
a := 5 a := 5
switch a { switch a {
case 5: return 2 case 5: return 2
} }
return 1 return 1
}`, }
`,
big.NewInt(2), big.NewInt(2),
}, },
{ {
"switch with no tag", "switch with no tag",
`package main `func f() bool { return false }
func f() bool { return false } func F%d() int {
func Main() int {
switch { switch {
case f(): case f():
return 1 return 1
@ -30,14 +36,14 @@ var switchTestCases = []testCase{
return 2 return 2
} }
return 3 return 3
}`, }
`,
big.NewInt(2), big.NewInt(2),
}, },
{ {
"type conversion in tag", "type conversion in tag",
`package main `type state int
type state int func F%d() int {
func Main() int {
a := 1 a := 1
switch state(a) { switch state(a) {
case 1: case 1:
@ -45,52 +51,52 @@ var switchTestCases = []testCase{
default: default:
return 11 return 11
} }
}`, }
`,
big.NewInt(42), big.NewInt(42),
}, },
{ {
"simple switch fail", "simple switch fail",
`package main `func F%d() int {
func Main() int {
a := 6 a := 6
switch a { switch a {
case 5: case 5:
return 2 return 2
} }
return 1 return 1
}`, }
`,
big.NewInt(1), big.NewInt(1),
}, },
{ {
"multiple cases success", "multiple cases success",
`package main `func F%d() int {
func Main() int {
a := 6 a := 6
switch a { switch a {
case 5: return 2 case 5: return 2
case 6: return 3 case 6: return 3
} }
return 1 return 1
}`, }
`,
big.NewInt(3), big.NewInt(3),
}, },
{ {
"multiple cases fail", "multiple cases fail",
`package main `func F%d() int {
func Main() int {
a := 7 a := 7
switch a { switch a {
case 5: return 2 case 5: return 2
case 6: return 3 case 6: return 3
} }
return 1 return 1
}`, }
`,
big.NewInt(1), big.NewInt(1),
}, },
{ {
"default case", "default case",
`package main `func F%d() int {
func Main() int {
a := 7 a := 7
switch a { switch a {
case 5: return 2 case 5: return 2
@ -98,13 +104,13 @@ var switchTestCases = []testCase{
default: return 4 default: return 4
} }
return 1 return 1
}`, }
`,
big.NewInt(4), big.NewInt(4),
}, },
{ {
"empty case before default", "empty case before default",
`package main `func F%d() int {
func Main() int {
a := 6 a := 6
switch a { switch a {
case 5: return 2 case 5: return 2
@ -112,13 +118,13 @@ var switchTestCases = []testCase{
default: return 4 default: return 4
} }
return 1 return 1
}`, }
`,
big.NewInt(1), big.NewInt(1),
}, },
{ {
"expression in case clause", "expression in case clause",
`package main `func F%d() int {
func Main() int {
a := 6 a := 6
b := 3 b := 3
switch a { switch a {
@ -126,13 +132,13 @@ var switchTestCases = []testCase{
case b*3-3: return 3 case b*3-3: return 3
} }
return 1 return 1
}`, }
`,
big.NewInt(3), big.NewInt(3),
}, },
{ {
"multiple expressions in case", "multiple expressions in case",
`package main `func F%d() int {
func Main() int {
a := 8 a := 8
b := 3 b := 3
switch a { switch a {
@ -140,13 +146,13 @@ var switchTestCases = []testCase{
case b*3-3, 7, 8: return 3 case b*3-3, 7, 8: return 3
} }
return 1 return 1
}`, }
`,
big.NewInt(3), big.NewInt(3),
}, },
{ {
"string switch", "string switch",
`package main `func F%d() int {
func Main() int {
name := "Valera" name := "Valera"
switch name { switch name {
case "Misha": return 2 case "Misha": return 2
@ -154,13 +160,13 @@ var switchTestCases = []testCase{
case "Lera", "Valer" + "a": return 4 case "Lera", "Valer" + "a": return 4
} }
return 1 return 1
}`, }
`,
big.NewInt(4), big.NewInt(4),
}, },
{ {
"break from switch", "break from switch",
`package main `func F%d() int {
func Main() int {
i := 3 i := 3
switch i { switch i {
case 2: return 2 case 2: return 2
@ -171,13 +177,13 @@ var switchTestCases = []testCase{
case 4: return 4 case 4: return 4
} }
return i return i
}`, }
`,
big.NewInt(1), big.NewInt(1),
}, },
{ {
"break from outer for", "break from outer for",
`package main `func F%d() int {
func Main() int {
i := 3 i := 3
loop: loop:
for i < 10 { for i < 10 {
@ -191,13 +197,13 @@ var switchTestCases = []testCase{
} }
} }
return i return i
}`, }
`,
big.NewInt(7), big.NewInt(7),
}, },
{ {
"continue outer for", "continue outer for",
`package main `func F%d() int {
func Main() int {
i := 2 i := 2
for i < 10 { for i < 10 {
i++ i++
@ -214,13 +220,13 @@ var switchTestCases = []testCase{
} }
} }
return i return i
}`, }
`,
big.NewInt(2), big.NewInt(2),
}, },
{ {
"simple fallthrough", "simple fallthrough",
`package main `func F%d() int {
func Main() int {
n := 2 n := 2
switch n { switch n {
case 1: return 5 case 1: return 5
@ -228,13 +234,13 @@ var switchTestCases = []testCase{
case 3: return 6 case 3: return 6
} }
return 7 return 7
}`, }
`,
big.NewInt(6), big.NewInt(6),
}, },
{ {
"double fallthrough", "double fallthrough",
`package main `func F%d() int {
func Main() int {
n := 2 n := 2
k := 5 k := 5
switch n { switch n {
@ -249,15 +255,26 @@ var switchTestCases = []testCase{
return k return k
} }
return k return k
}`, }
`,
big.NewInt(6), big.NewInt(6),
}, },
} }
func TestSwitch(t *testing.T) { 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) { 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)
}) })
} }
} }

View file

@ -1,6 +1,7 @@
package compiler_test package compiler_test
import ( import (
"bytes"
"errors" "errors"
"fmt" "fmt"
"math/big" "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. // It will be set in test and we want to fail if calling invalid syscall.
fs.Func = nil 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 { for goName, tc := range interops {
t.Run(goName, func(t *testing.T) { 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)) syscallID := interopnames.ToID([]byte(tc.method))
f := ic.GetFunction(syscallID) f := ic.GetFunction(syscallID)
require.NotNil(t, f) 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 }() defer func() { f.Func = nil }()
srcTmpl := `package foo invokeMethod(t, realName, script, ic.VM, debugInfo)
import "github.com/nspcc-dev/neo-go/pkg/interop/%s" require.NoError(t, ic.VM.Run())
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())
require.True(t, called) require.True(t, called)
if tc.isVoid { if tc.isVoid {
require.Equal(t, 0, v.Estack().Len()) require.Equal(t, 0, ic.VM.Estack().Len())
} else { } else {
require.Equal(t, 1, v.Estack().Len()) require.Equal(t, 1, ic.VM.Estack().Len())
require.Equal(t, big.NewInt(42), v.Estack().Pop().Value()) require.Equal(t, big.NewInt(42), ic.VM.Estack().Pop().Value())
} }
} }