Merge pull request #1957 from nspcc-dev/compiler/static
Emit debug info for static variables
This commit is contained in:
commit
fbcb08c5f0
3 changed files with 60 additions and 23 deletions
|
@ -44,6 +44,12 @@ type codegen struct {
|
||||||
scope *funcScope
|
scope *funcScope
|
||||||
|
|
||||||
globals map[string]int
|
globals map[string]int
|
||||||
|
// staticVariables contains global (static in NDX-DN11) variable names and types.
|
||||||
|
staticVariables []string
|
||||||
|
// initVariables contains variables local to `_initialize` method.
|
||||||
|
initVariables []string
|
||||||
|
// deployVariables contains variables local to `_initialize` method.
|
||||||
|
deployVariables []string
|
||||||
|
|
||||||
// A mapping from label's names to their ids.
|
// A mapping from label's names to their ids.
|
||||||
labels map[labelWithType]uint16
|
labels map[labelWithType]uint16
|
||||||
|
@ -458,6 +464,12 @@ func (c *codegen) convertFuncDecl(file ast.Node, decl *ast.FuncDecl, pkg *types.
|
||||||
emit.Opcodes(c.prog.BinWriter, opcode.RET)
|
emit.Opcodes(c.prog.BinWriter, opcode.RET)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if isInit {
|
||||||
|
c.initVariables = append(c.initVariables, f.variables...)
|
||||||
|
} else if isDeploy {
|
||||||
|
c.deployVariables = append(c.deployVariables, f.variables...)
|
||||||
|
}
|
||||||
|
|
||||||
f.rng.End = uint16(c.prog.Len() - 1)
|
f.rng.End = uint16(c.prog.Len() - 1)
|
||||||
|
|
||||||
if !isLambda {
|
if !isLambda {
|
||||||
|
|
|
@ -24,6 +24,8 @@ type DebugInfo struct {
|
||||||
Events []EventDebugInfo `json:"events"`
|
Events []EventDebugInfo `json:"events"`
|
||||||
// EmittedEvents contains events occurring in code.
|
// EmittedEvents contains events occurring in code.
|
||||||
EmittedEvents map[string][][]string `json:"-"`
|
EmittedEvents map[string][][]string `json:"-"`
|
||||||
|
// StaticVariables contains list of static variable names and types.
|
||||||
|
StaticVariables []string `json:"static-variables"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// MethodDebugInfo represents smart-contract's method debug information.
|
// MethodDebugInfo represents smart-contract's method debug information.
|
||||||
|
@ -115,9 +117,10 @@ func (c *codegen) saveSequencePoint(n ast.Node) {
|
||||||
|
|
||||||
func (c *codegen) emitDebugInfo(contract []byte) *DebugInfo {
|
func (c *codegen) emitDebugInfo(contract []byte) *DebugInfo {
|
||||||
d := &DebugInfo{
|
d := &DebugInfo{
|
||||||
MainPkg: c.mainPkg.Pkg.Name(),
|
MainPkg: c.mainPkg.Pkg.Name(),
|
||||||
Events: []EventDebugInfo{},
|
Events: []EventDebugInfo{},
|
||||||
Documents: c.documents,
|
Documents: c.documents,
|
||||||
|
StaticVariables: c.staticVariables,
|
||||||
}
|
}
|
||||||
if c.initEndOffset > 0 {
|
if c.initEndOffset > 0 {
|
||||||
d.Methods = append(d.Methods, MethodDebugInfo{
|
d.Methods = append(d.Methods, MethodDebugInfo{
|
||||||
|
@ -135,6 +138,7 @@ func (c *codegen) emitDebugInfo(contract []byte) *DebugInfo {
|
||||||
ReturnType: "Void",
|
ReturnType: "Void",
|
||||||
ReturnTypeSC: smartcontract.VoidType,
|
ReturnTypeSC: smartcontract.VoidType,
|
||||||
SeqPoints: c.sequencePoints["init"],
|
SeqPoints: c.sequencePoints["init"],
|
||||||
|
Variables: c.initVariables,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if c.deployEndOffset >= 0 {
|
if c.deployEndOffset >= 0 {
|
||||||
|
@ -165,6 +169,7 @@ func (c *codegen) emitDebugInfo(contract []byte) *DebugInfo {
|
||||||
ReturnType: "Void",
|
ReturnType: "Void",
|
||||||
ReturnTypeSC: smartcontract.VoidType,
|
ReturnTypeSC: smartcontract.VoidType,
|
||||||
SeqPoints: c.sequencePoints[manifest.MethodDeploy],
|
SeqPoints: c.sequencePoints[manifest.MethodDeploy],
|
||||||
|
Variables: c.deployVariables,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
for name, scope := range c.funcs {
|
for name, scope := range c.funcs {
|
||||||
|
@ -179,11 +184,11 @@ func (c *codegen) emitDebugInfo(contract []byte) *DebugInfo {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *codegen) registerDebugVariable(name string, expr ast.Expr) {
|
func (c *codegen) registerDebugVariable(name string, expr ast.Expr) {
|
||||||
|
_, vt := c.scAndVMTypeFromExpr(expr)
|
||||||
if c.scope == nil {
|
if c.scope == nil {
|
||||||
// do not save globals for now
|
c.staticVariables = append(c.staticVariables, name+","+vt.String())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
_, vt := c.scAndVMTypeFromExpr(expr)
|
|
||||||
c.scope.variables = append(c.scope.variables, name+","+vt.String())
|
c.scope.variables = append(c.scope.variables, name+","+vt.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,16 @@ func TestCodeGen_DebugInfo(t *testing.T) {
|
||||||
import "github.com/nspcc-dev/neo-go/pkg/interop"
|
import "github.com/nspcc-dev/neo-go/pkg/interop"
|
||||||
import "github.com/nspcc-dev/neo-go/pkg/interop/storage"
|
import "github.com/nspcc-dev/neo-go/pkg/interop/storage"
|
||||||
import "github.com/nspcc-dev/neo-go/pkg/interop/native/ledger"
|
import "github.com/nspcc-dev/neo-go/pkg/interop/native/ledger"
|
||||||
|
var staticVar int
|
||||||
|
func init() {
|
||||||
|
a := 1
|
||||||
|
_ = a
|
||||||
|
}
|
||||||
|
func init() {
|
||||||
|
x := ""
|
||||||
|
_ = x
|
||||||
|
staticVar = 1
|
||||||
|
}
|
||||||
func Main(op string) bool {
|
func Main(op string) bool {
|
||||||
var s string
|
var s string
|
||||||
_ = s
|
_ = s
|
||||||
|
@ -53,7 +63,7 @@ func MethodParams(addr interop.Hash160, h interop.Hash256,
|
||||||
type MyStruct struct {}
|
type MyStruct struct {}
|
||||||
func (ms MyStruct) MethodOnStruct() { }
|
func (ms MyStruct) MethodOnStruct() { }
|
||||||
func (ms *MyStruct) MethodOnPointerToStruct() { }
|
func (ms *MyStruct) MethodOnPointerToStruct() { }
|
||||||
func _deploy(data interface{}, isUpdate bool) {}
|
func _deploy(data interface{}, isUpdate bool) { x := 1; _ = x }
|
||||||
`
|
`
|
||||||
|
|
||||||
info, err := getBuildInfo("foo.go", src)
|
info, err := getBuildInfo("foo.go", src)
|
||||||
|
@ -79,6 +89,7 @@ func _deploy(data interface{}, isUpdate bool) {}
|
||||||
"MethodOnPointerToStruct": "Void",
|
"MethodOnPointerToStruct": "Void",
|
||||||
"MethodParams": "Boolean",
|
"MethodParams": "Boolean",
|
||||||
"_deploy": "Void",
|
"_deploy": "Void",
|
||||||
|
manifest.MethodInit: "Void",
|
||||||
}
|
}
|
||||||
for i := range d.Methods {
|
for i := range d.Methods {
|
||||||
name := d.Methods[i].ID
|
name := d.Methods[i].ID
|
||||||
|
@ -88,7 +99,9 @@ func _deploy(data interface{}, isUpdate bool) {}
|
||||||
|
|
||||||
t.Run("variables", func(t *testing.T) {
|
t.Run("variables", func(t *testing.T) {
|
||||||
vars := map[string][]string{
|
vars := map[string][]string{
|
||||||
"Main": {"s,ByteString", "res,Integer"},
|
"Main": {"s,ByteString", "res,Integer"},
|
||||||
|
manifest.MethodInit: {"a,Integer", "x,ByteString"},
|
||||||
|
manifest.MethodDeploy: {"x,Integer"},
|
||||||
}
|
}
|
||||||
for i := range d.Methods {
|
for i := range d.Methods {
|
||||||
v, ok := vars[d.Methods[i].ID]
|
v, ok := vars[d.Methods[i].ID]
|
||||||
|
@ -98,6 +111,10 @@ func _deploy(data interface{}, isUpdate bool) {}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("static variables", func(t *testing.T) {
|
||||||
|
require.Equal(t, []string{"staticVar,Integer"}, d.StaticVariables)
|
||||||
|
})
|
||||||
|
|
||||||
t.Run("param types", func(t *testing.T) {
|
t.Run("param types", func(t *testing.T) {
|
||||||
paramTypes := map[string][]DebugParam{
|
paramTypes := map[string][]DebugParam{
|
||||||
"_deploy": {
|
"_deploy": {
|
||||||
|
@ -158,14 +175,17 @@ func _deploy(data interface{}, isUpdate bool) {}
|
||||||
t.Run("convert to Manifest", func(t *testing.T) {
|
t.Run("convert to Manifest", func(t *testing.T) {
|
||||||
actual, err := d.ConvertToManifest(&Options{Name: "MyCTR", SafeMethods: []string{"methodInt", "methodString"}})
|
actual, err := d.ConvertToManifest(&Options{Name: "MyCTR", SafeMethods: []string{"methodInt", "methodString"}})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
// note: offsets are hard to predict, so we just take them from the output
|
|
||||||
expected := &manifest.Manifest{
|
expected := &manifest.Manifest{
|
||||||
Name: "MyCTR",
|
Name: "MyCTR",
|
||||||
ABI: manifest.ABI{
|
ABI: manifest.ABI{
|
||||||
Methods: []manifest.Method{
|
Methods: []manifest.Method{
|
||||||
{
|
{
|
||||||
Name: "_deploy",
|
Name: manifest.MethodInit,
|
||||||
Offset: 0,
|
Parameters: []manifest.Parameter{},
|
||||||
|
ReturnType: smartcontract.VoidType,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "_deploy",
|
||||||
Parameters: []manifest.Parameter{
|
Parameters: []manifest.Parameter{
|
||||||
manifest.NewParameter("data", smartcontract.AnyType),
|
manifest.NewParameter("data", smartcontract.AnyType),
|
||||||
manifest.NewParameter("isUpdate", smartcontract.BoolType),
|
manifest.NewParameter("isUpdate", smartcontract.BoolType),
|
||||||
|
@ -173,16 +193,14 @@ func _deploy(data interface{}, isUpdate bool) {}
|
||||||
ReturnType: smartcontract.VoidType,
|
ReturnType: smartcontract.VoidType,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "main",
|
Name: "main",
|
||||||
Offset: 4,
|
|
||||||
Parameters: []manifest.Parameter{
|
Parameters: []manifest.Parameter{
|
||||||
manifest.NewParameter("op", smartcontract.StringType),
|
manifest.NewParameter("op", smartcontract.StringType),
|
||||||
},
|
},
|
||||||
ReturnType: smartcontract.BoolType,
|
ReturnType: smartcontract.BoolType,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "methodInt",
|
Name: "methodInt",
|
||||||
Offset: 70,
|
|
||||||
Parameters: []manifest.Parameter{
|
Parameters: []manifest.Parameter{
|
||||||
{
|
{
|
||||||
Name: "a",
|
Name: "a",
|
||||||
|
@ -194,32 +212,27 @@ func _deploy(data interface{}, isUpdate bool) {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "methodString",
|
Name: "methodString",
|
||||||
Offset: 101,
|
|
||||||
Parameters: []manifest.Parameter{},
|
Parameters: []manifest.Parameter{},
|
||||||
ReturnType: smartcontract.StringType,
|
ReturnType: smartcontract.StringType,
|
||||||
Safe: true,
|
Safe: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "methodByteArray",
|
Name: "methodByteArray",
|
||||||
Offset: 107,
|
|
||||||
Parameters: []manifest.Parameter{},
|
Parameters: []manifest.Parameter{},
|
||||||
ReturnType: smartcontract.ByteArrayType,
|
ReturnType: smartcontract.ByteArrayType,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "methodArray",
|
Name: "methodArray",
|
||||||
Offset: 112,
|
|
||||||
Parameters: []manifest.Parameter{},
|
Parameters: []manifest.Parameter{},
|
||||||
ReturnType: smartcontract.ArrayType,
|
ReturnType: smartcontract.ArrayType,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "methodStruct",
|
Name: "methodStruct",
|
||||||
Offset: 117,
|
|
||||||
Parameters: []manifest.Parameter{},
|
Parameters: []manifest.Parameter{},
|
||||||
ReturnType: smartcontract.ArrayType,
|
ReturnType: smartcontract.ArrayType,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "methodConcat",
|
Name: "methodConcat",
|
||||||
Offset: 92,
|
|
||||||
Parameters: []manifest.Parameter{
|
Parameters: []manifest.Parameter{
|
||||||
{
|
{
|
||||||
Name: "a",
|
Name: "a",
|
||||||
|
@ -237,8 +250,7 @@ func _deploy(data interface{}, isUpdate bool) {}
|
||||||
ReturnType: smartcontract.StringType,
|
ReturnType: smartcontract.StringType,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "methodParams",
|
Name: "methodParams",
|
||||||
Offset: 129,
|
|
||||||
Parameters: []manifest.Parameter{
|
Parameters: []manifest.Parameter{
|
||||||
manifest.NewParameter("addr", smartcontract.Hash160Type),
|
manifest.NewParameter("addr", smartcontract.Hash160Type),
|
||||||
manifest.NewParameter("h", smartcontract.Hash256Type),
|
manifest.NewParameter("h", smartcontract.Hash256Type),
|
||||||
|
@ -267,7 +279,15 @@ func _deploy(data interface{}, isUpdate bool) {}
|
||||||
},
|
},
|
||||||
Extra: json.RawMessage("null"),
|
Extra: json.RawMessage("null"),
|
||||||
}
|
}
|
||||||
require.ElementsMatch(t, expected.ABI.Methods, actual.ABI.Methods)
|
require.Equal(t, len(expected.ABI.Methods), len(actual.ABI.Methods))
|
||||||
|
for _, exp := range expected.ABI.Methods {
|
||||||
|
md := actual.ABI.GetMethod(exp.Name, len(exp.Parameters))
|
||||||
|
require.NotNil(t, md)
|
||||||
|
require.Equal(t, exp.Name, md.Name)
|
||||||
|
require.Equal(t, exp.Parameters, md.Parameters)
|
||||||
|
require.Equal(t, exp.ReturnType, md.ReturnType)
|
||||||
|
require.Equal(t, exp.Safe, md.Safe)
|
||||||
|
}
|
||||||
require.Equal(t, expected.ABI.Events, actual.ABI.Events)
|
require.Equal(t, expected.ABI.Events, actual.ABI.Events)
|
||||||
require.Equal(t, expected.Groups, actual.Groups)
|
require.Equal(t, expected.Groups, actual.Groups)
|
||||||
require.Equal(t, expected.Permissions, actual.Permissions)
|
require.Equal(t, expected.Permissions, actual.Permissions)
|
||||||
|
|
Loading…
Reference in a new issue