mirror of
https://github.com/go-delve/delve.git
synced 2025-10-29 17:56:45 +08:00
proc/variables: bugfix: ifaces with types in user defined packages
The concrete type of an interface only contains the abbreviated package name, we must construct a map from package names to package paths to be able to resolve the concrete type of an interface.
This commit is contained in:
@ -2,6 +2,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"go/constant"
|
||||||
"runtime"
|
"runtime"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
@ -109,15 +110,20 @@ func main() {
|
|||||||
var err2 error = c1.pb
|
var err2 error = c1.pb
|
||||||
var errnil error = nil
|
var errnil error = nil
|
||||||
var iface1 interface{} = c1.sa[0]
|
var iface1 interface{} = c1.sa[0]
|
||||||
|
var iface2 interface{} = "test"
|
||||||
|
var iface3 interface{} = map[string]constant.Value{}
|
||||||
|
var iface4 interface{} = []constant.Value{constant.MakeInt64(4)}
|
||||||
var ifacenil interface{} = nil
|
var ifacenil interface{} = nil
|
||||||
arr1 := [4]int{0, 1, 2, 3}
|
arr1 := [4]int{0, 1, 2, 3}
|
||||||
parr := &arr1
|
parr := &arr1
|
||||||
cpx1 := complex(1, 2)
|
cpx1 := complex(1, 2)
|
||||||
|
const1 := constant.MakeInt64(3)
|
||||||
|
|
||||||
var amb1 = 1
|
var amb1 = 1
|
||||||
runtime.Breakpoint()
|
runtime.Breakpoint()
|
||||||
for amb1 := 0; amb1 < 10; amb1++ {
|
for amb1 := 0; amb1 < 10; amb1++ {
|
||||||
fmt.Println(amb1)
|
fmt.Println(amb1)
|
||||||
}
|
}
|
||||||
fmt.Println(i1, i2, i3, p1, amb1, s1, s3, a1, p2, p3, s2, as1, str1, f1, fn1, fn2, nilslice, nilptr, ch1, chnil, m1, mnil, m2, m3, up1, i4, i5, i6, err1, err2, errnil, iface1, ifacenil, arr1, parr, cpx1)
|
runtime.Breakpoint()
|
||||||
|
fmt.Println(i1, i2, i3, p1, amb1, s1, s3, a1, p2, p3, s2, as1, str1, f1, fn1, fn2, nilslice, nilptr, ch1, chnil, m1, mnil, m2, m3, up1, i4, i5, i6, err1, err2, errnil, iface1, iface2, ifacenil, arr1, parr, cpx1, const1, iface3, iface4)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -932,15 +932,6 @@ func equalChildren(xv, yv *Variable, shortcircuit bool) (bool, error) {
|
|||||||
return r, nil
|
return r, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dbp *Process) findType(name string) (dwarf.Type, error) {
|
|
||||||
reader := dbp.DwarfReader()
|
|
||||||
typentry, err := reader.SeekToTypeNamed(name)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return dbp.dwarf.Type(typentry.Offset)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *Variable) asInt() (int64, error) {
|
func (v *Variable) asInt() (int64, error) {
|
||||||
if v.DwarfType == nil {
|
if v.DwarfType == nil {
|
||||||
if v.Value.Kind() != constant.Int {
|
if v.Value.Kind() != constant.Int {
|
||||||
|
|||||||
@ -39,6 +39,9 @@ type Process struct {
|
|||||||
// Normally SelectedGoroutine is CurrentThread.GetG, it will not be only if SwitchGoroutine is called with a goroutine that isn't attached to a thread
|
// Normally SelectedGoroutine is CurrentThread.GetG, it will not be only if SwitchGoroutine is called with a goroutine that isn't attached to a thread
|
||||||
SelectedGoroutine *G
|
SelectedGoroutine *G
|
||||||
|
|
||||||
|
// Maps package names to package paths, needed to lookup types inside DWARF info
|
||||||
|
packageMap map[string]string
|
||||||
|
|
||||||
allGCache []*G
|
allGCache []*G
|
||||||
dwarf *dwarf.Data
|
dwarf *dwarf.Data
|
||||||
goSymTable *gosym.Table
|
goSymTable *gosym.Table
|
||||||
|
|||||||
123
proc/types.go
Normal file
123
proc/types.go
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
package proc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"debug/dwarf"
|
||||||
|
"go/ast"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Do not call this function directly it isn't able to deal correctly with package paths
|
||||||
|
func (dbp *Process) findType(name string) (dwarf.Type, error) {
|
||||||
|
reader := dbp.DwarfReader()
|
||||||
|
typentry, err := reader.SeekToTypeNamed(name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return dbp.dwarf.Type(typentry.Offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dbp *Process) pointerTo(typ dwarf.Type) dwarf.Type {
|
||||||
|
return &dwarf.PtrType{dwarf.CommonType{int64(dbp.arch.PtrSize()), ""}, typ}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dbp *Process) findTypeExpr(expr ast.Expr) (dwarf.Type, error) {
|
||||||
|
dbp.loadPackageMap()
|
||||||
|
dbp.expandPackagesInType(expr)
|
||||||
|
if snode, ok := expr.(*ast.StarExpr); ok {
|
||||||
|
// Pointer types only appear in the dwarf informations when
|
||||||
|
// a pointer to the type is used in the target program, here
|
||||||
|
// we create a pointer type on the fly so that the user can
|
||||||
|
// specify a pointer to any variable used in the target program
|
||||||
|
ptyp, err := dbp.findType(exprToString(snode.X))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return dbp.pointerTo(ptyp), nil
|
||||||
|
}
|
||||||
|
return dbp.findType(exprToString(expr))
|
||||||
|
}
|
||||||
|
|
||||||
|
func complexType(typename string) bool {
|
||||||
|
dot := 0
|
||||||
|
for _, ch := range typename {
|
||||||
|
switch ch {
|
||||||
|
case '*', '[', '<', '{', '(', ' ':
|
||||||
|
return true
|
||||||
|
case '.':
|
||||||
|
dot++
|
||||||
|
if dot > 1 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dbp *Process) loadPackageMap() error {
|
||||||
|
if dbp.packageMap != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
dbp.packageMap = map[string]string{}
|
||||||
|
reader := dbp.DwarfReader()
|
||||||
|
for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if entry.Tag != dwarf.TagTypedef && entry.Tag != dwarf.TagBaseType && entry.Tag != dwarf.TagClassType && entry.Tag != dwarf.TagStructType {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
typename, ok := entry.Val(dwarf.AttrName).(string)
|
||||||
|
if !ok || complexType(typename) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
dot := strings.Index(typename, ".")
|
||||||
|
if dot < 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
path := typename[:dot]
|
||||||
|
slash := strings.LastIndex(path, "/")
|
||||||
|
if slash < 0 || slash+1 >= len(path) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
name := path[slash+1:]
|
||||||
|
dbp.packageMap[name] = path
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dbp *Process) expandPackagesInType(expr ast.Expr) {
|
||||||
|
switch e := expr.(type) {
|
||||||
|
case *ast.ArrayType:
|
||||||
|
dbp.expandPackagesInType(e.Elt)
|
||||||
|
case *ast.ChanType:
|
||||||
|
dbp.expandPackagesInType(e.Value)
|
||||||
|
case *ast.FuncType:
|
||||||
|
for i := range e.Params.List {
|
||||||
|
dbp.expandPackagesInType(e.Params.List[i].Type)
|
||||||
|
}
|
||||||
|
for i := range e.Results.List {
|
||||||
|
dbp.expandPackagesInType(e.Results.List[i].Type)
|
||||||
|
}
|
||||||
|
case *ast.MapType:
|
||||||
|
dbp.expandPackagesInType(e.Key)
|
||||||
|
dbp.expandPackagesInType(e.Value)
|
||||||
|
case *ast.ParenExpr:
|
||||||
|
dbp.expandPackagesInType(e.X)
|
||||||
|
case *ast.SelectorExpr:
|
||||||
|
switch x := e.X.(type) {
|
||||||
|
case *ast.Ident:
|
||||||
|
if path, ok := dbp.packageMap[x.Name]; ok {
|
||||||
|
x.Name = path
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
dbp.expandPackagesInType(e.X)
|
||||||
|
}
|
||||||
|
case *ast.StarExpr:
|
||||||
|
dbp.expandPackagesInType(e.X)
|
||||||
|
default:
|
||||||
|
// nothing to do
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -5,7 +5,6 @@ import (
|
|||||||
"debug/dwarf"
|
"debug/dwarf"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/ast"
|
|
||||||
"go/constant"
|
"go/constant"
|
||||||
"go/parser"
|
"go/parser"
|
||||||
"go/token"
|
"go/token"
|
||||||
@ -1451,10 +1450,16 @@ func (v *Variable) loadInterface(recurseLevel int, loadData bool) {
|
|||||||
|
|
||||||
typ, err := v.thread.dbp.findTypeExpr(t)
|
typ, err := v.thread.dbp.findTypeExpr(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
v.Unreadable = fmt.Errorf("invalid interface type: %v", err)
|
v.Unreadable = fmt.Errorf("interface type \"%s\" not found for 0x%x: %v", constant.StringVal(typestring.Value), data.Addr, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
realtyp := resolveTypedef(typ)
|
||||||
|
if _, isptr := realtyp.(*dwarf.PtrType); !isptr {
|
||||||
|
// interface to non-pointer types are pointers even if the type says otherwise
|
||||||
|
typ = v.thread.dbp.pointerTo(typ)
|
||||||
|
}
|
||||||
|
|
||||||
data = newVariable("data", data.Addr, typ, data.thread)
|
data = newVariable("data", data.Addr, typ, data.thread)
|
||||||
|
|
||||||
v.Children = []Variable{*data}
|
v.Children = []Variable{*data}
|
||||||
@ -1464,21 +1469,6 @@ func (v *Variable) loadInterface(recurseLevel int, loadData bool) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dbp *Process) findTypeExpr(expr ast.Expr) (dwarf.Type, error) {
|
|
||||||
if snode, ok := expr.(*ast.StarExpr); ok {
|
|
||||||
// Pointer types only appear in the dwarf informations when
|
|
||||||
// a pointer to the type is used in the target program, here
|
|
||||||
// we create a pointer type on the fly so that the user can
|
|
||||||
// specify a pointer to any variable used in the target program
|
|
||||||
ptyp, err := dbp.findType(exprToString(snode.X))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &dwarf.PtrType{dwarf.CommonType{int64(dbp.arch.PtrSize()), exprToString(expr)}, ptyp}, nil
|
|
||||||
}
|
|
||||||
return dbp.findType(exprToString(expr))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fetches all variables of a specific type in the current function scope
|
// Fetches all variables of a specific type in the current function scope
|
||||||
func (scope *EvalScope) variablesByTag(tag dwarf.Tag) ([]*Variable, error) {
|
func (scope *EvalScope) variablesByTag(tag dwarf.Tag) ([]*Variable, error) {
|
||||||
reader := scope.DwarfReader()
|
reader := scope.DwarfReader()
|
||||||
|
|||||||
@ -404,6 +404,9 @@ func TestEvalExpression(t *testing.T) {
|
|||||||
{"err2", true, "error(*struct main.bstruct) *{a: main.astruct {A: 1, B: 2}}", "", "error", nil},
|
{"err2", true, "error(*struct main.bstruct) *{a: main.astruct {A: 1, B: 2}}", "", "error", nil},
|
||||||
{"errnil", true, "error nil", "", "error", nil},
|
{"errnil", true, "error nil", "", "error", nil},
|
||||||
{"iface1", true, "interface {}(*struct main.astruct) *{A: 1, B: 2}", "", "interface {}", nil},
|
{"iface1", true, "interface {}(*struct main.astruct) *{A: 1, B: 2}", "", "interface {}", nil},
|
||||||
|
{"iface2", true, "interface {}(*struct string) *\"test\"", "", "interface {}", nil},
|
||||||
|
{"iface3", true, "interface {}(map[string]go/constant.Value) []", "", "interface {}", nil},
|
||||||
|
{"iface4", true, "interface {}(*struct []go/constant.Value) *[*4]", "", "interface {}", nil},
|
||||||
{"ifacenil", true, "interface {} nil", "", "interface {}", nil},
|
{"ifacenil", true, "interface {} nil", "", "interface {}", nil},
|
||||||
{"err1 == err2", false, "false", "", "", nil},
|
{"err1 == err2", false, "false", "", "", nil},
|
||||||
{"err1 == iface1", false, "", "", "", fmt.Errorf("mismatched types \"error\" and \"interface {}\"")},
|
{"err1 == iface1", false, "", "", "", fmt.Errorf("mismatched types \"error\" and \"interface {}\"")},
|
||||||
@ -412,6 +415,7 @@ func TestEvalExpression(t *testing.T) {
|
|||||||
{"err1.(*main.astruct)", false, "*struct main.astruct {A: 1, B: 2}", "", "*struct main.astruct", nil},
|
{"err1.(*main.astruct)", false, "*struct main.astruct {A: 1, B: 2}", "", "*struct main.astruct", nil},
|
||||||
{"err1.(*main.bstruct)", false, "", "", "", fmt.Errorf("interface conversion: error is *struct main.astruct, not *struct main.bstruct")},
|
{"err1.(*main.bstruct)", false, "", "", "", fmt.Errorf("interface conversion: error is *struct main.astruct, not *struct main.bstruct")},
|
||||||
{"errnil.(*main.astruct)", false, "", "", "", fmt.Errorf("interface conversion: error is nil, not *main.astruct")},
|
{"errnil.(*main.astruct)", false, "", "", "", fmt.Errorf("interface conversion: error is nil, not *main.astruct")},
|
||||||
|
{"const1", true, "go/constant.Value(*go/constant.int64Val) *3", "", "go/constant.Value", nil},
|
||||||
|
|
||||||
// combined expressions
|
// combined expressions
|
||||||
{"c1.pb.a.A", true, "1", "", "int", nil},
|
{"c1.pb.a.A", true, "1", "", "int", nil},
|
||||||
|
|||||||
Reference in New Issue
Block a user