mirror of
https://github.com/go-delve/delve.git
synced 2025-10-28 12:47:22 +08:00
Initial refactor of source package
Major source cleanup, still not finished. Removes gross control flow.
This commit is contained in:
@ -1,8 +1,6 @@
|
|||||||
package proc
|
package proc
|
||||||
|
|
||||||
import (
|
import "encoding/binary"
|
||||||
"encoding/binary"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Takes an offset from RSP and returns the address of the
|
// Takes an offset from RSP and returns the address of the
|
||||||
// instruction the currect function is going to return to.
|
// instruction the currect function is going to return to.
|
||||||
@ -78,7 +76,6 @@ func (dbp *Process) stacktrace(pc, sp uint64, depth int) ([]Location, error) {
|
|||||||
if fn != nil && fn.Name == "runtime.goexit" {
|
if fn != nil && fn.Name == "runtime.goexit" {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
return locations, nil
|
return locations, nil
|
||||||
}
|
}
|
||||||
|
|||||||
221
source/source.go
221
source/source.go
@ -26,11 +26,11 @@ func (n NoNodeError) Error() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Returns the first node at the given file:line.
|
// Returns the first node at the given file:line.
|
||||||
func (s *Searcher) FirstNodeAt(fname string, line int) (ast.Node, error) {
|
func (s *Searcher) FirstNodeAt(fname string, line int) (*ast.File, ast.Node, error) {
|
||||||
var node ast.Node
|
var node ast.Node
|
||||||
f, err := s.parse(fname)
|
f, err := s.parse(fname)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
ast.Inspect(f, func(n ast.Node) bool {
|
ast.Inspect(f, func(n ast.Node) bool {
|
||||||
if n == nil {
|
if n == nil {
|
||||||
@ -44,9 +44,9 @@ func (s *Searcher) FirstNodeAt(fname string, line int) (ast.Node, error) {
|
|||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
if node == nil {
|
if node == nil {
|
||||||
return nil, NoNodeError{f: fname, l: line}
|
return nil, nil, NoNodeError{f: fname, l: line}
|
||||||
}
|
}
|
||||||
return node, nil
|
return f, node, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type Done string
|
type Done string
|
||||||
@ -58,96 +58,29 @@ func (d Done) Error() string {
|
|||||||
// Returns all possible lines that could be executed after the given file:line,
|
// Returns all possible lines that could be executed after the given file:line,
|
||||||
// within the same source file.
|
// within the same source file.
|
||||||
func (s *Searcher) NextLines(fname string, line int) (lines []int, err error) {
|
func (s *Searcher) NextLines(fname string, line int) (lines []int, err error) {
|
||||||
var found bool
|
parsedFile, n, err := s.FirstNodeAt(fname, line)
|
||||||
n, err := s.FirstNodeAt(fname, line)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return lines, nil
|
return lines, nil
|
||||||
}
|
}
|
||||||
defer func() {
|
|
||||||
if e := recover(); e != nil {
|
|
||||||
e = e.(Done)
|
|
||||||
nl := make([]int, 0, len(lines))
|
|
||||||
fnd := make(map[int]bool)
|
|
||||||
for _, l := range lines {
|
|
||||||
if _, ok := fnd[l]; !ok {
|
|
||||||
fnd[l] = true
|
|
||||||
nl = append(nl, l)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lines = nl
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
switch x := n.(type) {
|
switch x := n.(type) {
|
||||||
// Check if we are at an 'if' statement.
|
// Follow if statements
|
||||||
//
|
|
||||||
// If we are at an 'if' statement, employ the following algorithm:
|
|
||||||
// * Follow all 'else if' statements, appending their line number
|
|
||||||
// * Follow any 'else' statement if it exists, appending the line
|
|
||||||
// number of the statement following the 'else'.
|
|
||||||
// * If there is no 'else' statement, append line of first statement
|
|
||||||
// following the entire 'if' block.
|
|
||||||
case *ast.IfStmt:
|
case *ast.IfStmt:
|
||||||
var rbrace int
|
lines = removeDuplicateLines(s.parseIfStmtBlock(x, parsedFile))
|
||||||
p := x.Body.List[0].Pos()
|
return
|
||||||
pos := s.fileset.Position(p)
|
|
||||||
lines = append(lines, pos.Line)
|
|
||||||
|
|
||||||
if x.Else == nil {
|
|
||||||
// Grab first line after entire 'if' block
|
|
||||||
rbrace = s.fileset.Position(x.Body.Rbrace).Line
|
|
||||||
f, err := s.parse(fname)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
ast.Inspect(f, func(n ast.Node) bool {
|
|
||||||
if n == nil {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
pos := s.fileset.Position(n.Pos())
|
|
||||||
if rbrace < pos.Line {
|
|
||||||
lines = append(lines, pos.Line)
|
|
||||||
panic(Done("done"))
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
// Follow any 'else' statements
|
|
||||||
for {
|
|
||||||
if stmt, ok := x.Else.(*ast.IfStmt); ok {
|
|
||||||
pos := s.fileset.Position(stmt.Pos())
|
|
||||||
lines = append(lines, pos.Line)
|
|
||||||
x = stmt
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if x.Else != nil {
|
|
||||||
pos := s.fileset.Position(x.Else.Pos())
|
|
||||||
ast.Inspect(x, func(n ast.Node) bool {
|
|
||||||
if found {
|
|
||||||
panic(Done("done"))
|
|
||||||
}
|
|
||||||
if n == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
p := s.fileset.Position(n.Pos())
|
|
||||||
if pos.Line < p.Line {
|
|
||||||
lines = append(lines, p.Line)
|
|
||||||
found = true
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Follow case statements.
|
// Follow case statements.
|
||||||
//
|
|
||||||
// Append line for first statement following each 'case' condition.
|
// Append line for first statement following each 'case' condition.
|
||||||
case *ast.SwitchStmt:
|
case *ast.SwitchStmt:
|
||||||
|
var switchEnd int
|
||||||
ast.Inspect(x, func(n ast.Node) bool {
|
ast.Inspect(x, func(n ast.Node) bool {
|
||||||
if stmt, ok := n.(*ast.SwitchStmt); ok {
|
if stmt, ok := n.(*ast.SwitchStmt); ok {
|
||||||
ast.Inspect(stmt, func(n ast.Node) bool {
|
switchEnd = s.fileset.Position(stmt.End()).Line
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if switchEnd < s.fileset.Position(x.Pos()).Line {
|
||||||
|
return false
|
||||||
|
}
|
||||||
if stmt, ok := n.(*ast.CaseClause); ok {
|
if stmt, ok := n.(*ast.CaseClause); ok {
|
||||||
p := stmt.Body[0].Pos()
|
p := stmt.Body[0].Pos()
|
||||||
pos := s.fileset.Position(p)
|
pos := s.fileset.Position(p)
|
||||||
@ -156,35 +89,95 @@ func (s *Searcher) NextLines(fname string, line int) (lines []int, err error) {
|
|||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
panic(Done("done"))
|
lines = removeDuplicateLines(lines)
|
||||||
|
return
|
||||||
|
// Default case - find next source line.
|
||||||
|
default:
|
||||||
|
lines = removeDuplicateLines(s.parseDefaultBlock(x, parsedFile, line))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return lines, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parses file named by fname, caching files it has already parsed.
|
||||||
|
func (s *Searcher) parse(fname string) (*ast.File, error) {
|
||||||
|
if f, ok := s.visited[fname]; ok {
|
||||||
|
return f, nil
|
||||||
|
}
|
||||||
|
f, err := parser.ParseFile(s.fileset, fname, nil, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
s.visited[fname] = f
|
||||||
|
return f, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Searcher) nextLineAfter(parsedFile *ast.File, line int) (nextLine int) {
|
||||||
|
var done bool
|
||||||
|
ast.Inspect(parsedFile, func(n ast.Node) bool {
|
||||||
|
if done || n == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
pos := s.fileset.Position(n.Pos())
|
||||||
|
if line < pos.Line {
|
||||||
|
nextLine = pos.Line
|
||||||
|
done = true
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
// Default case - find next source line.
|
return
|
||||||
//
|
}
|
||||||
// We are not at a branch, employ the following algorithm:
|
|
||||||
// * Traverse tree, storing any loop as a parent
|
// If we are at an 'if' statement, employ the following algorithm:
|
||||||
// * Find next source line after the given line
|
// * Follow all 'else if' statements, appending their line number
|
||||||
// * Check and see if we've passed the scope of any parent we've
|
// * Follow any 'else' statement if it exists, appending the line
|
||||||
// stored. If so, pop them off the stack. The last parent that
|
// number of the statement following the 'else'.
|
||||||
// is left get's appending to our list of lines since we could
|
// * If there is no 'else' statement, append line of first statement
|
||||||
// end up at the top of the loop again.
|
// following the entire 'if' block.
|
||||||
default:
|
func (s *Searcher) parseIfStmtBlock(ifRoot *ast.IfStmt, parsedFile *ast.File) []int {
|
||||||
var (
|
var (
|
||||||
|
rbrace int
|
||||||
|
ifStmtLine = s.fileset.Position(ifRoot.Body.List[0].Pos()).Line
|
||||||
|
lines = []int{ifStmtLine}
|
||||||
|
)
|
||||||
|
|
||||||
|
for {
|
||||||
|
if ifRoot.Else == nil {
|
||||||
|
// Grab first line after entire 'if' block
|
||||||
|
rbrace = s.fileset.Position(ifRoot.Body.Rbrace).Line
|
||||||
|
return append(lines, s.nextLineAfter(parsedFile, rbrace))
|
||||||
|
}
|
||||||
|
// Continue following 'else if' branches.
|
||||||
|
if elseStmt, ok := ifRoot.Else.(*ast.IfStmt); ok {
|
||||||
|
lines = append(lines, s.fileset.Position(elseStmt.Pos()).Line)
|
||||||
|
ifRoot = elseStmt
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Grab next line after final 'else'.
|
||||||
|
pos := s.fileset.Position(ifRoot.Else.Pos())
|
||||||
|
return append(lines, s.nextLineAfter(parsedFile, pos.Line))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We are not at a branch, employ the following algorithm:
|
||||||
|
// * Traverse tree, storing any loop as a parent
|
||||||
|
// * Find next source line after the given line
|
||||||
|
// * Check and see if we've passed the scope of any parent we've
|
||||||
|
// stored. If so, pop them off the stack. The last parent that
|
||||||
|
// is left get's appending to our list of lines since we could
|
||||||
|
// end up at the top of the loop again.
|
||||||
|
func (s *Searcher) parseDefaultBlock(ifRoot ast.Node, parsedFile *ast.File, line int) []int {
|
||||||
|
var (
|
||||||
|
found bool
|
||||||
|
lines []int
|
||||||
parents []*ast.BlockStmt
|
parents []*ast.BlockStmt
|
||||||
parentBlockBeginLine int
|
parentBlockBeginLine int
|
||||||
deferEndLine int
|
deferEndLine int
|
||||||
)
|
)
|
||||||
f, err := s.parse(fname)
|
ast.Inspect(parsedFile, func(n ast.Node) bool {
|
||||||
if err != nil {
|
if found || n == nil {
|
||||||
return nil, err
|
return false
|
||||||
}
|
|
||||||
ast.Inspect(f, func(n ast.Node) bool {
|
|
||||||
if found {
|
|
||||||
panic(Done("done"))
|
|
||||||
}
|
|
||||||
if n == nil {
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pos := s.fileset.Position(n.Pos())
|
pos := s.fileset.Position(n.Pos())
|
||||||
@ -257,7 +250,7 @@ func (s *Searcher) NextLines(fname string, line int) (lines []int, err error) {
|
|||||||
beginLine int
|
beginLine int
|
||||||
)
|
)
|
||||||
|
|
||||||
ast.Inspect(f, func(n ast.Node) bool {
|
ast.Inspect(parsedFile, func(n ast.Node) bool {
|
||||||
if n == nil || endfound {
|
if n == nil || endfound {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -297,19 +290,17 @@ func (s *Searcher) NextLines(fname string, line int) (lines []int, err error) {
|
|||||||
pos := s.fileset.Position(parent.List[0].Pos())
|
pos := s.fileset.Position(parent.List[0].Pos())
|
||||||
lines = append(lines, lbrace, pos.Line)
|
lines = append(lines, lbrace, pos.Line)
|
||||||
}
|
}
|
||||||
}
|
return lines
|
||||||
return lines, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parses file named by fname, caching files it has already parsed.
|
func removeDuplicateLines(lines []int) []int {
|
||||||
func (s *Searcher) parse(fname string) (*ast.File, error) {
|
nl := make([]int, 0, len(lines))
|
||||||
if f, ok := s.visited[fname]; ok {
|
fnd := make(map[int]bool)
|
||||||
return f, nil
|
for _, l := range lines {
|
||||||
|
if _, ok := fnd[l]; !ok {
|
||||||
|
fnd[l] = true
|
||||||
|
nl = append(nl, l)
|
||||||
}
|
}
|
||||||
f, err := parser.ParseFile(s.fileset, fname, nil, 0)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
s.visited[fname] = f
|
return nl
|
||||||
return f, nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,7 +12,7 @@ func TestTokenAtLine(t *testing.T) {
|
|||||||
tf, _ = filepath.Abs("../_fixtures/testvisitorprog.go")
|
tf, _ = filepath.Abs("../_fixtures/testvisitorprog.go")
|
||||||
v = New()
|
v = New()
|
||||||
)
|
)
|
||||||
n, err := v.FirstNodeAt(tf, 8)
|
_, n, err := v.FirstNodeAt(tf, 8)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -52,9 +52,9 @@ func TestNextLines(t *testing.T) {
|
|||||||
fmt.Println(lines)
|
fmt.Println(lines)
|
||||||
t.Fatalf("did not get correct number of lines back expected %d got %d for test case %d", len(c.nextlines), len(lines), i+1)
|
t.Fatalf("did not get correct number of lines back expected %d got %d for test case %d", len(c.nextlines), len(lines), i+1)
|
||||||
}
|
}
|
||||||
for i, l := range lines {
|
for j, l := range lines {
|
||||||
if l != c.nextlines[i] {
|
if l != c.nextlines[j] {
|
||||||
t.Fatalf("expected index %d to be %d got %d", i, c.nextlines[i], l)
|
t.Fatalf("expected index %d to be %d got %d for case %d", j, c.nextlines[j], l, i+1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user