364 lines
8.3 KiB
Go
Executable File
364 lines
8.3 KiB
Go
Executable File
package compiler_test
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"fmt"
|
|
"math/big"
|
|
"strings"
|
|
"testing"
|
|
|
|
"git.marketally.com/tutus-one/tutus-chain/pkg/compiler"
|
|
"git.marketally.com/tutus-one/tutus-chain/pkg/smartcontract/trigger"
|
|
"git.marketally.com/tutus-one/tutus-chain/pkg/vm"
|
|
"git.marketally.com/tutus-one/tutus-chain/pkg/vm/stackitem"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
type convertTestCase struct {
|
|
returnType string
|
|
argValue string
|
|
result any
|
|
}
|
|
|
|
func getFunctionName(typ string) string {
|
|
switch typ {
|
|
case "bool":
|
|
return "Bool"
|
|
case "[]byte":
|
|
return "Bytes"
|
|
case "int":
|
|
return "Integer"
|
|
}
|
|
panic("invalid type")
|
|
}
|
|
|
|
func TestConvert(t *testing.T) {
|
|
srcTmpl := `func F%d() %s {
|
|
arg := %s
|
|
return convert.To%s(arg)
|
|
}
|
|
`
|
|
|
|
convertTestCases := []convertTestCase{
|
|
{"bool", "true", true},
|
|
{"bool", "false", false},
|
|
{"bool", "12", true},
|
|
{"bool", "0", false},
|
|
{"bool", "[]byte{0, 1, 0}", true},
|
|
{"bool", "[]byte{0}", true},
|
|
{"bool", `""`, false},
|
|
{"int", "true", big.NewInt(1)},
|
|
{"int", "false", big.NewInt(0)},
|
|
{"int", "12", big.NewInt(12)},
|
|
{"int", "0", big.NewInt(0)},
|
|
{"int", "[]byte{0, 1, 0}", big.NewInt(256)},
|
|
{"int", "[]byte{0}", big.NewInt(0)},
|
|
{"[]byte", "true", []byte{1}},
|
|
{"[]byte", "false", []byte{0}},
|
|
{"[]byte", "12", []byte{0x0C}},
|
|
{"[]byte", "0", []byte{}},
|
|
{"[]byte", "[]byte{0, 1, 0}", []byte{0, 1, 0}},
|
|
}
|
|
|
|
srcBuilder := bytes.NewBuffer([]byte(`package testcase
|
|
import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/convert"
|
|
`))
|
|
for i, tc := range convertTestCases {
|
|
name := getFunctionName(tc.returnType)
|
|
_, err := fmt.Fprintf(srcBuilder, srcTmpl, i, tc.returnType, tc.argValue, name)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
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.Reset(trigger.Application)
|
|
invokeMethod(t, fmt.Sprintf("F%d", i), ne.Script, v, di)
|
|
runAndCheck(t, v, tc.result)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestTypeAssertion(t *testing.T) {
|
|
t.Run("inside return statement", func(t *testing.T) {
|
|
src := `package foo
|
|
func Main() int {
|
|
a := []byte{1}
|
|
var u any
|
|
u = a
|
|
return u.(int)
|
|
}`
|
|
eval(t, src, big.NewInt(1))
|
|
})
|
|
t.Run("inside general declaration", func(t *testing.T) {
|
|
src := `package foo
|
|
func Main() int {
|
|
a := []byte{1}
|
|
var u any
|
|
u = a
|
|
var ret = u.(int)
|
|
return ret
|
|
}`
|
|
eval(t, src, big.NewInt(1))
|
|
})
|
|
t.Run("inside assignment statement", func(t *testing.T) {
|
|
src := `package foo
|
|
func Main() int {
|
|
a := []byte{1}
|
|
var u any
|
|
u = a
|
|
var ret int
|
|
ret = u.(int)
|
|
return ret
|
|
}`
|
|
eval(t, src, big.NewInt(1))
|
|
})
|
|
t.Run("inside definition statement", func(t *testing.T) {
|
|
src := `package foo
|
|
func Main() int {
|
|
a := []byte{1}
|
|
var u any
|
|
u = a
|
|
ret := u.(int)
|
|
return ret
|
|
}`
|
|
eval(t, src, big.NewInt(1))
|
|
})
|
|
}
|
|
|
|
func TestTypeAssertionWithOK(t *testing.T) {
|
|
t.Run("inside general declaration", func(t *testing.T) {
|
|
src := `package foo
|
|
func Main() bool {
|
|
a := 1
|
|
var u any
|
|
u = a
|
|
var _, ok = u.(int) // *ast.GenDecl
|
|
return ok
|
|
}`
|
|
_, _, err := compiler.CompileWithOptions("foo.go", strings.NewReader(src), nil)
|
|
require.Error(t, err)
|
|
require.ErrorIs(t, err, compiler.ErrUnsupportedTypeAssertion)
|
|
})
|
|
t.Run("inside assignment statement", func(t *testing.T) {
|
|
src := `package foo
|
|
func Main() bool {
|
|
a := 1
|
|
var u any
|
|
u = a
|
|
var ok bool
|
|
_, ok = u.(int) // *ast.AssignStmt
|
|
return ok
|
|
}`
|
|
_, _, err := compiler.CompileWithOptions("foo.go", strings.NewReader(src), nil)
|
|
require.Error(t, err)
|
|
require.ErrorIs(t, err, compiler.ErrUnsupportedTypeAssertion)
|
|
})
|
|
t.Run("inside definition statement", func(t *testing.T) {
|
|
src := `package foo
|
|
func Main() bool {
|
|
a := 1
|
|
var u any
|
|
u = a
|
|
_, ok := u.(int) // *ast.AssignStmt
|
|
return ok
|
|
}`
|
|
_, _, err := compiler.CompileWithOptions("foo.go", strings.NewReader(src), nil)
|
|
require.Error(t, err)
|
|
require.ErrorIs(t, err, compiler.ErrUnsupportedTypeAssertion)
|
|
})
|
|
}
|
|
|
|
func TestTypeConversion(t *testing.T) {
|
|
src := `package foo
|
|
type myInt int
|
|
func Main() int32 {
|
|
var a int32 = 41
|
|
b := myInt(a)
|
|
incMy := func(x myInt) myInt { return x + 1 }
|
|
c := incMy(b)
|
|
return int32(c)
|
|
}`
|
|
|
|
eval(t, src, big.NewInt(42))
|
|
}
|
|
|
|
func TestSelectorTypeConversion(t *testing.T) {
|
|
src := `package foo
|
|
import "git.marketally.com/tutus-one/tutus-chain/pkg/compiler/testdata/types"
|
|
import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/util"
|
|
import "git.marketally.com/tutus-one/tutus-chain/pkg/interop"
|
|
func Main() int {
|
|
var a int
|
|
if util.Equals(types.Buffer(nil), nil) {
|
|
a += 1
|
|
}
|
|
|
|
// Buffer != ByteArray
|
|
if util.Equals(types.Buffer("\x12"), "\x12") {
|
|
a += 10
|
|
}
|
|
|
|
tmp := []byte{0x23}
|
|
if util.Equals(types.ByteString(tmp), "\x23") {
|
|
a += 100
|
|
}
|
|
|
|
addr := "aaaaaaaaaaaaaaaaaaaa"
|
|
buf := []byte(addr)
|
|
if util.Equals(interop.Hash160(addr), interop.Hash160(buf)) {
|
|
a += 1000
|
|
}
|
|
return a
|
|
}`
|
|
eval(t, src, big.NewInt(1101))
|
|
}
|
|
|
|
func TestTypeConversionString(t *testing.T) {
|
|
src := `package foo
|
|
type mystr string
|
|
func Main() mystr {
|
|
b := []byte{'l', 'a', 'm', 'a', 'o'}
|
|
s := mystr(b)
|
|
b[0] = 'u'
|
|
return s
|
|
}`
|
|
eval(t, src, []byte("lamao"))
|
|
}
|
|
|
|
func TestInterfaceTypeConversion(t *testing.T) {
|
|
src := `package foo
|
|
func Main() int {
|
|
a := 1
|
|
b := any(a).(int)
|
|
return b
|
|
}`
|
|
eval(t, src, big.NewInt(1))
|
|
}
|
|
|
|
func TestConvert_Uint(t *testing.T) {
|
|
nums := []uint64{
|
|
0,
|
|
1,
|
|
127,
|
|
128,
|
|
255,
|
|
256,
|
|
1024,
|
|
1<<16 - 1,
|
|
1 << 16,
|
|
1<<32 - 1,
|
|
1 << 32,
|
|
1<<64 - 1,
|
|
}
|
|
|
|
var (
|
|
countUint64 = len(nums)
|
|
countUint32 = len(nums) - 2
|
|
countUint16 = len(nums) - 4
|
|
countUint8 = 5
|
|
)
|
|
|
|
testCases := []struct {
|
|
nums []uint64
|
|
offset int
|
|
typ string
|
|
helperType string
|
|
endians map[string]func([]byte, uint64)
|
|
}{
|
|
{
|
|
nums: nums[:countUint64],
|
|
typ: "uint64",
|
|
helperType: "Uint64",
|
|
endians: map[string]func([]byte, uint64){
|
|
"LE": binary.LittleEndian.PutUint64,
|
|
"BE": binary.BigEndian.PutUint64,
|
|
},
|
|
},
|
|
{
|
|
nums: nums[:countUint32],
|
|
offset: 4,
|
|
typ: "uint32",
|
|
helperType: "Uint32",
|
|
endians: map[string]func([]byte, uint64){
|
|
"LE": binary.LittleEndian.PutUint64,
|
|
"BE": binary.BigEndian.PutUint64,
|
|
},
|
|
},
|
|
{
|
|
nums: nums[:countUint16],
|
|
offset: 6,
|
|
typ: "uint16",
|
|
helperType: "Uint16",
|
|
endians: map[string]func([]byte, uint64){
|
|
"LE": binary.LittleEndian.PutUint64,
|
|
"BE": binary.BigEndian.PutUint64,
|
|
},
|
|
},
|
|
{
|
|
nums: nums[:countUint8],
|
|
offset: 7,
|
|
typ: "uint8",
|
|
helperType: "Uint8",
|
|
endians: map[string]func([]byte, uint64){
|
|
"": binary.LittleEndian.PutUint64,
|
|
},
|
|
},
|
|
}
|
|
|
|
srcToBytesTmpl := `package foo
|
|
import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/convert"
|
|
|
|
func Main(args []any) []byte {
|
|
return convert.%sToBytes%s(args[0].(%s))
|
|
}
|
|
`
|
|
|
|
srcFromBytesTmpl := `package foo
|
|
import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/convert"
|
|
|
|
func Main(args []any) %s {
|
|
return convert.Bytes%sTo%s(args[0].([]byte))
|
|
}
|
|
`
|
|
|
|
srcCompatibilityCheckTmpl := `package foo
|
|
import "git.marketally.com/tutus-one/tutus-chain/pkg/interop/convert"
|
|
func Main(args []any) %s {
|
|
data := convert.%sToBytes%s(args[0].(%s))
|
|
return convert.Bytes%sTo%s(data)
|
|
}
|
|
`
|
|
|
|
for _, tc := range testCases {
|
|
for endian, convertFunc := range tc.endians {
|
|
srcToBytes := fmt.Sprintf(srcToBytesTmpl, tc.helperType, endian, tc.typ)
|
|
srcFromBytes := fmt.Sprintf(srcFromBytesTmpl, tc.typ, endian, tc.helperType)
|
|
srcCompatibilityCheck := fmt.Sprintf(srcCompatibilityCheckTmpl,
|
|
tc.typ, tc.helperType, endian, tc.typ, endian, tc.helperType,
|
|
)
|
|
|
|
buf := make([]byte, 8)
|
|
data := buf[:8-tc.offset]
|
|
if endian == "BE" {
|
|
data = buf[tc.offset:]
|
|
}
|
|
|
|
for _, num := range tc.nums {
|
|
convertFunc(buf, num)
|
|
evalWithArgs(t, srcToBytes, nil, []stackitem.Item{stackitem.Make(num)}, data)
|
|
evalWithArgs(t, srcFromBytes, nil, []stackitem.Item{stackitem.Make(data)},
|
|
new(big.Int).SetUint64(num),
|
|
)
|
|
evalWithArgs(t, srcCompatibilityCheck, nil, []stackitem.Item{stackitem.Make(num)},
|
|
new(big.Int).SetUint64(num),
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|