2020-03-31 12:56:10 +00:00
|
|
|
package compiler
|
|
|
|
|
|
|
|
import (
|
|
|
|
"testing"
|
|
|
|
|
compiler: add ability to generate .abi.json file
A part of integration with NEO Blockchain Toolkit (see #902). To be
able to deploy smart-contract compiled with neo-go compiler via NEO
Express, we have to generate additional .abi.json file. This file
contains the following information:
- hash of the compiled contract
- smart-contract metadata (title, description, version, author,
email, has-storage, has-dynamic-invoke, is-payable)
- smart-contract entry point
- functions
- events
However, this .abi.json file is slightly different from the one,
described in manifest.go, so we have to add auxilaury stractures for
json marshalling. The .abi.json format used by NEO-Express is described
[here](https://github.com/neo-project/neo-devpack-dotnet/blob/master/src/Neo.Compiler.MSIL/FuncExport.cs#L66).
2020-04-28 16:39:01 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
2020-03-31 12:56:10 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/internal/testserdes"
|
compiler: add ability to generate .abi.json file
A part of integration with NEO Blockchain Toolkit (see #902). To be
able to deploy smart-contract compiled with neo-go compiler via NEO
Express, we have to generate additional .abi.json file. This file
contains the following information:
- hash of the compiled contract
- smart-contract metadata (title, description, version, author,
email, has-storage, has-dynamic-invoke, is-payable)
- smart-contract entry point
- functions
- events
However, this .abi.json file is slightly different from the one,
described in manifest.go, so we have to add auxilaury stractures for
json marshalling. The .abi.json format used by NEO-Express is described
[here](https://github.com/neo-project/neo-devpack-dotnet/blob/master/src/Neo.Compiler.MSIL/FuncExport.cs#L66).
2020-04-28 16:39:01 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
2020-06-24 04:22:33 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
2020-03-31 13:16:32 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
"github.com/stretchr/testify/require"
|
2020-03-31 12:56:10 +00:00
|
|
|
)
|
|
|
|
|
2020-03-31 13:16:32 +00:00
|
|
|
func TestCodeGen_DebugInfo(t *testing.T) {
|
|
|
|
src := `package foo
|
|
|
|
func Main(op string) bool {
|
2020-04-02 13:36:11 +00:00
|
|
|
var s string
|
|
|
|
_ = s
|
2020-03-31 13:16:32 +00:00
|
|
|
res := methodInt(op)
|
|
|
|
_ = methodString()
|
|
|
|
_ = methodByteArray()
|
|
|
|
_ = methodArray()
|
|
|
|
_ = methodStruct()
|
|
|
|
return res == 42
|
|
|
|
}
|
|
|
|
|
|
|
|
func methodInt(a string) int {
|
|
|
|
if a == "get42" {
|
|
|
|
return 42
|
|
|
|
}
|
|
|
|
return 3
|
|
|
|
}
|
2020-04-29 13:32:07 +00:00
|
|
|
func methodConcat(a, b string, c string) string{
|
|
|
|
return a + b + c
|
|
|
|
}
|
2020-03-31 13:16:32 +00:00
|
|
|
func methodString() string { return "" }
|
|
|
|
func methodByteArray() []byte { return nil }
|
|
|
|
func methodArray() []bool { return nil }
|
|
|
|
func methodStruct() struct{} { return struct{}{} }
|
|
|
|
`
|
|
|
|
|
|
|
|
info, err := getBuildInfo(src)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
pkg := info.program.Package(info.initialPackage)
|
|
|
|
c := newCodegen(info, pkg)
|
|
|
|
require.NoError(t, c.compile(info, pkg))
|
|
|
|
|
|
|
|
buf := c.prog.Bytes()
|
2020-06-24 04:22:33 +00:00
|
|
|
d := c.emitDebugInfo(buf)
|
2020-03-31 13:16:32 +00:00
|
|
|
require.NotNil(t, d)
|
|
|
|
|
2020-06-24 04:22:33 +00:00
|
|
|
t.Run("hash", func(t *testing.T) {
|
|
|
|
require.True(t, hash.Hash160(buf).Equals(d.Hash))
|
|
|
|
})
|
|
|
|
|
2020-03-31 13:16:32 +00:00
|
|
|
t.Run("return types", func(t *testing.T) {
|
|
|
|
returnTypes := map[string]string{
|
|
|
|
"methodInt": "Integer",
|
2020-04-29 13:32:07 +00:00
|
|
|
"methodConcat": "String",
|
2020-03-31 13:16:32 +00:00
|
|
|
"methodString": "String", "methodByteArray": "ByteArray",
|
|
|
|
"methodArray": "Array", "methodStruct": "Struct",
|
|
|
|
"Main": "Boolean",
|
|
|
|
}
|
|
|
|
for i := range d.Methods {
|
|
|
|
name := d.Methods[i].Name.Name
|
|
|
|
assert.Equal(t, returnTypes[name], d.Methods[i].ReturnType)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2020-04-02 13:36:11 +00:00
|
|
|
t.Run("variables", func(t *testing.T) {
|
|
|
|
vars := map[string][]string{
|
|
|
|
"Main": {"s,String", "res,Integer"},
|
|
|
|
}
|
|
|
|
for i := range d.Methods {
|
|
|
|
v, ok := vars[d.Methods[i].Name.Name]
|
|
|
|
if ok {
|
|
|
|
require.Equal(t, v, d.Methods[i].Variables)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2020-04-29 13:32:07 +00:00
|
|
|
t.Run("param types", func(t *testing.T) {
|
|
|
|
paramTypes := map[string][]DebugParam{
|
|
|
|
"methodInt": {{
|
|
|
|
Name: "a",
|
|
|
|
Type: "String",
|
|
|
|
}},
|
|
|
|
"methodConcat": {
|
|
|
|
{
|
|
|
|
Name: "a",
|
|
|
|
Type: "String",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "b",
|
|
|
|
Type: "String",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "c",
|
|
|
|
Type: "String",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
"Main": {{
|
|
|
|
Name: "op",
|
|
|
|
Type: "String",
|
|
|
|
}},
|
|
|
|
}
|
|
|
|
for i := range d.Methods {
|
|
|
|
v, ok := paramTypes[d.Methods[i].Name.Name]
|
|
|
|
if ok {
|
|
|
|
require.Equal(t, v, d.Methods[i].Parameters)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2020-03-31 13:16:32 +00:00
|
|
|
// basic check that last instruction of every method is indeed RET
|
|
|
|
for i := range d.Methods {
|
|
|
|
index := d.Methods[i].Range.End
|
|
|
|
require.True(t, int(index) < len(buf))
|
|
|
|
require.EqualValues(t, opcode.RET, buf[index])
|
|
|
|
}
|
compiler: add ability to generate .abi.json file
A part of integration with NEO Blockchain Toolkit (see #902). To be
able to deploy smart-contract compiled with neo-go compiler via NEO
Express, we have to generate additional .abi.json file. This file
contains the following information:
- hash of the compiled contract
- smart-contract metadata (title, description, version, author,
email, has-storage, has-dynamic-invoke, is-payable)
- smart-contract entry point
- functions
- events
However, this .abi.json file is slightly different from the one,
described in manifest.go, so we have to add auxilaury stractures for
json marshalling. The .abi.json format used by NEO-Express is described
[here](https://github.com/neo-project/neo-devpack-dotnet/blob/master/src/Neo.Compiler.MSIL/FuncExport.cs#L66).
2020-04-28 16:39:01 +00:00
|
|
|
|
|
|
|
t.Run("convert to ABI", func(t *testing.T) {
|
2020-06-24 04:22:33 +00:00
|
|
|
actual := d.convertToABI(smartcontract.HasStorage)
|
compiler: add ability to generate .abi.json file
A part of integration with NEO Blockchain Toolkit (see #902). To be
able to deploy smart-contract compiled with neo-go compiler via NEO
Express, we have to generate additional .abi.json file. This file
contains the following information:
- hash of the compiled contract
- smart-contract metadata (title, description, version, author,
email, has-storage, has-dynamic-invoke, is-payable)
- smart-contract entry point
- functions
- events
However, this .abi.json file is slightly different from the one,
described in manifest.go, so we have to add auxilaury stractures for
json marshalling. The .abi.json format used by NEO-Express is described
[here](https://github.com/neo-project/neo-devpack-dotnet/blob/master/src/Neo.Compiler.MSIL/FuncExport.cs#L66).
2020-04-28 16:39:01 +00:00
|
|
|
expected := ABI{
|
|
|
|
Hash: hash.Hash160(buf),
|
|
|
|
Metadata: Metadata{
|
|
|
|
HasStorage: true,
|
|
|
|
HasDynamicInvocation: false,
|
|
|
|
IsPayable: false,
|
|
|
|
},
|
|
|
|
EntryPoint: mainIdent,
|
|
|
|
Functions: []Method{
|
|
|
|
{
|
|
|
|
Name: mainIdent,
|
|
|
|
Parameters: []DebugParam{
|
|
|
|
{
|
|
|
|
Name: "op",
|
|
|
|
Type: "String",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
ReturnType: "Boolean",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Events: []Event{},
|
|
|
|
}
|
|
|
|
assert.Equal(t, expected, actual)
|
|
|
|
})
|
2020-03-31 13:16:32 +00:00
|
|
|
}
|
|
|
|
|
2020-03-31 13:57:35 +00:00
|
|
|
func TestSequencePoints(t *testing.T) {
|
|
|
|
src := `package foo
|
|
|
|
func Main(op string) bool {
|
|
|
|
if op == "123" {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}`
|
|
|
|
|
|
|
|
info, err := getBuildInfo(src)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
pkg := info.program.Package(info.initialPackage)
|
|
|
|
c := newCodegen(info, pkg)
|
|
|
|
require.NoError(t, c.compile(info, pkg))
|
|
|
|
|
2020-06-24 04:22:33 +00:00
|
|
|
buf := c.prog.Bytes()
|
|
|
|
d := c.emitDebugInfo(buf)
|
2020-03-31 13:57:35 +00:00
|
|
|
require.NotNil(t, d)
|
|
|
|
|
|
|
|
// Main func has 2 return on 4-th and 6-th lines.
|
|
|
|
ps := d.Methods[0].SeqPoints
|
|
|
|
require.Equal(t, 2, len(ps))
|
|
|
|
require.Equal(t, 4, ps[0].StartLine)
|
|
|
|
require.Equal(t, 6, ps[1].StartLine)
|
|
|
|
}
|
|
|
|
|
2020-03-31 12:56:10 +00:00
|
|
|
func TestDebugInfo_MarshalJSON(t *testing.T) {
|
|
|
|
d := &DebugInfo{
|
2020-06-24 05:55:24 +00:00
|
|
|
Hash: util.Uint160{10, 11, 12, 13},
|
|
|
|
Documents: []string{"/path/to/file"},
|
2020-03-31 12:56:10 +00:00
|
|
|
Methods: []MethodDebugInfo{
|
|
|
|
{
|
|
|
|
ID: "id1",
|
|
|
|
Name: DebugMethodName{
|
|
|
|
Namespace: "default",
|
|
|
|
Name: "method1",
|
|
|
|
},
|
|
|
|
Range: DebugRange{Start: 10, End: 20},
|
|
|
|
Parameters: []DebugParam{
|
|
|
|
{"param1", "Integer"},
|
|
|
|
{"ok", "Boolean"},
|
|
|
|
},
|
|
|
|
ReturnType: "ByteArray",
|
|
|
|
Variables: []string{},
|
|
|
|
SeqPoints: []DebugSeqPoint{
|
|
|
|
{
|
|
|
|
Opcode: 123,
|
|
|
|
Document: 1,
|
|
|
|
StartLine: 2,
|
|
|
|
StartCol: 3,
|
|
|
|
EndLine: 4,
|
|
|
|
EndCol: 5,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Events: []EventDebugInfo{},
|
|
|
|
}
|
|
|
|
|
|
|
|
testserdes.MarshalUnmarshalJSON(t, d, new(DebugInfo))
|
|
|
|
}
|