forked from TrueCloudLab/neoneo-go
compiler: support sequence points in debug info
Sequence points is a way to map a specific instruction offset from a compiled contract to a text span in a source file. This commit implements mapping only for `return` statements. Further improvements are straight-forward.
This commit is contained in:
parent
24fef35ead
commit
5d3da26e1e
3 changed files with 52 additions and 1 deletions
|
@ -52,6 +52,11 @@ type codegen struct {
|
|||
// A label to be used in the next statement.
|
||||
nextLabel string
|
||||
|
||||
// sequencePoints is mapping from method name to a slice
|
||||
// containing info about mapping from opcode's offset
|
||||
// to a text span in the source file.
|
||||
sequencePoints map[string][]DebugSeqPoint
|
||||
|
||||
// Label table for recording jump destinations.
|
||||
l []int
|
||||
}
|
||||
|
@ -258,6 +263,7 @@ func (c *codegen) convertFuncDecl(file ast.Node, decl *ast.FuncDecl) {
|
|||
|
||||
// If this function returns the void (no return stmt) we will cleanup its junk on the stack.
|
||||
if !hasReturnStmt(decl) {
|
||||
c.saveSequencePoint(decl.Body)
|
||||
emit.Opcode(c.prog.BinWriter, opcode.FROMALTSTACK)
|
||||
emit.Opcode(c.prog.BinWriter, opcode.DROP)
|
||||
emit.Opcode(c.prog.BinWriter, opcode.RET)
|
||||
|
@ -303,7 +309,7 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
|
|||
|
||||
case *ast.AssignStmt:
|
||||
multiRet := len(n.Rhs) != len(n.Lhs)
|
||||
|
||||
c.saveSequencePoint(n)
|
||||
for i := 0; i < len(n.Lhs); i++ {
|
||||
switch t := n.Lhs[i].(type) {
|
||||
case *ast.Ident:
|
||||
|
@ -407,6 +413,7 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
|
|||
ast.Walk(c, n.Results[i])
|
||||
}
|
||||
|
||||
c.saveSequencePoint(n)
|
||||
emit.Opcode(c.prog.BinWriter, opcode.FROMALTSTACK)
|
||||
emit.Opcode(c.prog.BinWriter, opcode.DROP) // Cleanup the stack.
|
||||
emit.Opcode(c.prog.BinWriter, opcode.RET)
|
||||
|
@ -648,6 +655,8 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
|
|||
return nil
|
||||
}
|
||||
|
||||
c.saveSequencePoint(n)
|
||||
|
||||
args := transformArgs(n.Fun, n.Args)
|
||||
|
||||
// Handle the arguments
|
||||
|
@ -1298,6 +1307,8 @@ func newCodegen(info *buildInfo, pkg *loader.PackageInfo) *codegen {
|
|||
funcs: map[string]*funcScope{},
|
||||
labels: map[labelWithType]uint16{},
|
||||
typeInfo: &pkg.Info,
|
||||
|
||||
sequencePoints: make(map[string][]DebugSeqPoint),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -76,6 +76,19 @@ type DebugParam struct {
|
|||
Type string
|
||||
}
|
||||
|
||||
func (c *codegen) saveSequencePoint(n ast.Node) {
|
||||
fset := c.buildInfo.program.Fset
|
||||
start := fset.Position(n.Pos())
|
||||
end := fset.Position(n.End())
|
||||
c.sequencePoints[c.scope.name] = append(c.sequencePoints[c.scope.name], DebugSeqPoint{
|
||||
Opcode: c.prog.Len(),
|
||||
StartLine: start.Line,
|
||||
StartCol: start.Offset,
|
||||
EndLine: end.Line,
|
||||
EndCol: end.Offset,
|
||||
})
|
||||
}
|
||||
|
||||
func (c *codegen) emitDebugInfo() *DebugInfo {
|
||||
d := &DebugInfo{
|
||||
EntryPoint: mainIdent,
|
||||
|
@ -108,6 +121,7 @@ func (c *codegen) methodInfoFromScope(name string, scope *funcScope) *MethodDebu
|
|||
Range: scope.rng,
|
||||
Parameters: params,
|
||||
ReturnType: c.scReturnTypeFromScope(scope),
|
||||
SeqPoints: c.sequencePoints[name],
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -64,6 +64,32 @@ func methodStruct() struct{} { return struct{}{} }
|
|||
}
|
||||
}
|
||||
|
||||
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))
|
||||
|
||||
d := c.emitDebugInfo()
|
||||
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)
|
||||
}
|
||||
|
||||
func TestDebugInfo_MarshalJSON(t *testing.T) {
|
||||
d := &DebugInfo{
|
||||
EntryPoint: "main",
|
||||
|
|
Loading…
Reference in a new issue