mirror of
https://github.com/go-delve/delve.git
synced 2025-10-29 01:27:16 +08:00
proc/variables: Support for interface types
This commit is contained in:
@ -26,6 +26,14 @@ func afunc(x int) int {
|
|||||||
|
|
||||||
type functype func(int) int
|
type functype func(int) int
|
||||||
|
|
||||||
|
func (a *astruct) Error() string {
|
||||||
|
return "not an error"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *bstruct) Error() string {
|
||||||
|
return "not an error"
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
i1 := 1
|
i1 := 1
|
||||||
i2 := 2
|
i2 := 2
|
||||||
@ -96,11 +104,16 @@ func main() {
|
|||||||
i4 := 800
|
i4 := 800
|
||||||
i5 := -3
|
i5 := -3
|
||||||
i6 := -500
|
i6 := -500
|
||||||
|
var err1 error = c1.sa[0]
|
||||||
|
var err2 error = c1.pb
|
||||||
|
var errnil error = nil
|
||||||
|
var iface1 interface{} = c1.sa[0]
|
||||||
|
var ifacenil interface{} = nil
|
||||||
|
|
||||||
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, a1, p2, p3, s2, as1, str1, f1, fn1, fn2, nilslice, nilptr, ch1, chnil, m1, mnil, m2, m3, up1, i4, i5, i6)
|
fmt.Println(i1, i2, i3, p1, amb1, s1, 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)
|
||||||
}
|
}
|
||||||
|
|||||||
61
proc/eval.go
61
proc/eval.go
@ -55,6 +55,9 @@ func (scope *EvalScope) evalAST(t ast.Expr) (*Variable, error) {
|
|||||||
// if it's not a package variable then it must be a struct member access
|
// if it's not a package variable then it must be a struct member access
|
||||||
return scope.evalStructSelector(node)
|
return scope.evalStructSelector(node)
|
||||||
|
|
||||||
|
case *ast.TypeAssertExpr: // <expression>.(<type>)
|
||||||
|
return scope.evalTypeAssert(node)
|
||||||
|
|
||||||
case *ast.IndexExpr:
|
case *ast.IndexExpr:
|
||||||
return scope.evalIndex(node)
|
return scope.evalIndex(node)
|
||||||
|
|
||||||
@ -166,26 +169,11 @@ func (scope *EvalScope) evalTypeCast(node *ast.CallExpr) (*Variable, error) {
|
|||||||
fnnode = p.X
|
fnnode = p.X
|
||||||
}
|
}
|
||||||
|
|
||||||
var styp, typ dwarf.Type
|
styp, err := scope.Thread.dbp.findTypeExpr(fnnode)
|
||||||
|
if err != nil {
|
||||||
if snode, ok := fnnode.(*ast.StarExpr); ok {
|
return nil, err
|
||||||
// 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 := scope.findType(exprToString(snode.X))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
typ = &dwarf.PtrType{dwarf.CommonType{int64(scope.Thread.dbp.arch.PtrSize()), exprToString(fnnode)}, ptyp}
|
|
||||||
styp = typ
|
|
||||||
} else {
|
|
||||||
styp, err = scope.findType(exprToString(fnnode))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
typ = resolveTypedef(styp)
|
|
||||||
}
|
}
|
||||||
|
typ := resolveTypedef(styp)
|
||||||
|
|
||||||
converr := fmt.Errorf("can not convert \"%s\" to %s", exprToString(node.Args[0]), typ.String())
|
converr := fmt.Errorf("can not convert \"%s\" to %s", exprToString(node.Args[0]), typ.String())
|
||||||
|
|
||||||
@ -312,6 +300,35 @@ func (scope *EvalScope) evalStructSelector(node *ast.SelectorExpr) (*Variable, e
|
|||||||
return xv.structMember(node.Sel.Name)
|
return xv.structMember(node.Sel.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Evaluates expressions <subexpr>.(<type>)
|
||||||
|
func (scope *EvalScope) evalTypeAssert(node *ast.TypeAssertExpr) (*Variable, error) {
|
||||||
|
xv, err := scope.evalAST(node.X)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if xv.Kind != reflect.Interface {
|
||||||
|
return nil, fmt.Errorf("expression \"%s\" not an interface", exprToString(node.X))
|
||||||
|
}
|
||||||
|
xv.loadInterface(0, false)
|
||||||
|
if xv.Unreadable != nil {
|
||||||
|
return nil, xv.Unreadable
|
||||||
|
}
|
||||||
|
if xv.Children[0].Unreadable != nil {
|
||||||
|
return nil, xv.Children[0].Unreadable
|
||||||
|
}
|
||||||
|
if xv.Children[0].Addr == 0 {
|
||||||
|
return nil, fmt.Errorf("interface conversion: %s is nil, not %s", xv.DwarfType.String(), exprToString(node.Type))
|
||||||
|
}
|
||||||
|
typ, err := scope.Thread.dbp.findTypeExpr(node.Type)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if xv.Children[0].DwarfType.String() != typ.String() {
|
||||||
|
return nil, fmt.Errorf("interface conversion: %s is %s, not %s", xv.DwarfType.String(), xv.Children[0].TypeString(), typ)
|
||||||
|
}
|
||||||
|
return &xv.Children[0], nil
|
||||||
|
}
|
||||||
|
|
||||||
// Evaluates expressions <subexpr>[<subexpr>] (subscript access to arrays, slices and maps)
|
// Evaluates expressions <subexpr>[<subexpr>] (subscript access to arrays, slices and maps)
|
||||||
func (scope *EvalScope) evalIndex(node *ast.IndexExpr) (*Variable, error) {
|
func (scope *EvalScope) evalIndex(node *ast.IndexExpr) (*Variable, error) {
|
||||||
xev, err := scope.evalAST(node.X)
|
xev, err := scope.evalAST(node.X)
|
||||||
@ -761,13 +778,13 @@ func equalChildren(xv, yv *Variable, shortcircuit bool) (bool, error) {
|
|||||||
return r, nil
|
return r, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (scope *EvalScope) findType(name string) (dwarf.Type, error) {
|
func (dbp *Process) findType(name string) (dwarf.Type, error) {
|
||||||
reader := scope.DwarfReader()
|
reader := dbp.DwarfReader()
|
||||||
typentry, err := reader.SeekToTypeNamed(name)
|
typentry, err := reader.SeekToTypeNamed(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return scope.Thread.dbp.dwarf.Type(typentry.Offset)
|
return dbp.dwarf.Type(typentry.Offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *Variable) asInt() (int64, error) {
|
func (v *Variable) asInt() (int64, error) {
|
||||||
|
|||||||
@ -5,6 +5,7 @@ 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"
|
||||||
@ -110,6 +111,14 @@ type EvalScope struct {
|
|||||||
CFA int64
|
CFA int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type IsNilErr struct {
|
||||||
|
name string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err *IsNilErr) Error() string {
|
||||||
|
return fmt.Sprintf("%s is nil", err.name)
|
||||||
|
}
|
||||||
|
|
||||||
func newVariable(name string, addr uintptr, dwarfType dwarf.Type, thread *Thread) *Variable {
|
func newVariable(name string, addr uintptr, dwarfType dwarf.Type, thread *Thread) *Variable {
|
||||||
v := &Variable{
|
v := &Variable{
|
||||||
Name: name,
|
Name: name,
|
||||||
@ -142,6 +151,8 @@ func newVariable(name string, addr uintptr, dwarfType dwarf.Type, thread *Thread
|
|||||||
if v.Addr != 0 {
|
if v.Addr != 0 {
|
||||||
v.base, v.Len, v.Unreadable = v.thread.readStringInfo(v.Addr)
|
v.base, v.Len, v.Unreadable = v.thread.readStringInfo(v.Addr)
|
||||||
}
|
}
|
||||||
|
case t.StructName == "runtime.iface" || t.StructName == "runtime.eface":
|
||||||
|
v.Kind = reflect.Interface
|
||||||
case strings.HasPrefix(t.StructName, "[]"):
|
case strings.HasPrefix(t.StructName, "[]"):
|
||||||
v.Kind = reflect.Slice
|
v.Kind = reflect.Slice
|
||||||
if v.Addr != 0 {
|
if v.Addr != 0 {
|
||||||
@ -249,7 +260,7 @@ func (v *Variable) toField(field *dwarf.StructField) (*Variable, error) {
|
|||||||
return v.clone(), nil
|
return v.clone(), nil
|
||||||
}
|
}
|
||||||
if v.Addr == 0 {
|
if v.Addr == 0 {
|
||||||
return nil, fmt.Errorf("%s is nil", v.Name)
|
return nil, &IsNilErr{v.Name}
|
||||||
}
|
}
|
||||||
|
|
||||||
name := ""
|
name := ""
|
||||||
@ -770,6 +781,9 @@ func (v *Variable) loadValueInternal(recurseLevel int) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case reflect.Interface:
|
||||||
|
v.loadInterface(recurseLevel, true)
|
||||||
|
|
||||||
case reflect.Complex64, reflect.Complex128:
|
case reflect.Complex64, reflect.Complex128:
|
||||||
v.readComplex(v.RealType.(*dwarf.ComplexType).ByteSize)
|
v.readComplex(v.RealType.(*dwarf.ComplexType).ByteSize)
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
@ -1366,6 +1380,101 @@ func mapEvacuated(b *Variable) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v *Variable) loadInterface(recurseLevel int, loadData bool) {
|
||||||
|
var typestring, data *Variable
|
||||||
|
isnil := false
|
||||||
|
|
||||||
|
for _, f := range v.RealType.(*dwarf.StructType).Field {
|
||||||
|
switch f.Name {
|
||||||
|
case "tab": // for runtime.iface
|
||||||
|
tab, _ := v.toField(f)
|
||||||
|
_type, err := tab.structMember("_type")
|
||||||
|
if err != nil {
|
||||||
|
_, isnil = err.(*IsNilErr)
|
||||||
|
if !isnil {
|
||||||
|
v.Unreadable = fmt.Errorf("invalid interface type: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
typestring, err = _type.structMember("_string")
|
||||||
|
if err != nil {
|
||||||
|
v.Unreadable = fmt.Errorf("invalid interface type: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
typestring = typestring.maybeDereference()
|
||||||
|
}
|
||||||
|
case "_type": // for runtime.eface
|
||||||
|
var err error
|
||||||
|
_type, _ := v.toField(f)
|
||||||
|
typestring, err = _type.structMember("_string")
|
||||||
|
if err != nil {
|
||||||
|
_, isnil = err.(*IsNilErr)
|
||||||
|
if !isnil {
|
||||||
|
v.Unreadable = fmt.Errorf("invalid interface type: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
typestring = typestring.maybeDereference()
|
||||||
|
}
|
||||||
|
case "data":
|
||||||
|
data, _ = v.toField(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if isnil {
|
||||||
|
// interface to nil
|
||||||
|
data = data.maybeDereference()
|
||||||
|
v.Children = []Variable{*data}
|
||||||
|
v.Children[0].loadValue()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if typestring == nil || data == nil || typestring.Addr == 0 || typestring.Kind != reflect.String {
|
||||||
|
v.Unreadable = fmt.Errorf("invalid interface type")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
typestring.loadValue()
|
||||||
|
if typestring.Unreadable != nil {
|
||||||
|
v.Unreadable = fmt.Errorf("invalid interface type: %v", typestring.Unreadable)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
t, err := parser.ParseExpr(constant.StringVal(typestring.Value))
|
||||||
|
if err != nil {
|
||||||
|
v.Unreadable = fmt.Errorf("invalid interface type, unparsable data type: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
typ, err := v.thread.dbp.findTypeExpr(t)
|
||||||
|
if err != nil {
|
||||||
|
v.Unreadable = fmt.Errorf("invalid interface type: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
data = newVariable("data", data.Addr, typ, data.thread)
|
||||||
|
|
||||||
|
v.Children = []Variable{*data}
|
||||||
|
if loadData {
|
||||||
|
v.Children[0].loadValue()
|
||||||
|
}
|
||||||
|
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()
|
||||||
|
|||||||
@ -68,10 +68,16 @@ func ConvertVar(v *proc.Variable) *Variable {
|
|||||||
|
|
||||||
if v.DwarfType != nil {
|
if v.DwarfType != nil {
|
||||||
r.Type = v.DwarfType.String()
|
r.Type = v.DwarfType.String()
|
||||||
|
if r.Type == "*void" {
|
||||||
|
r.Type = "unsafe.Pointer"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if v.RealType != nil {
|
if v.RealType != nil {
|
||||||
r.RealType = v.RealType.String()
|
r.RealType = v.RealType.String()
|
||||||
|
if r.RealType == "*void" {
|
||||||
|
r.Type = "unsafe.Pointer"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if v.Unreadable != nil {
|
if v.Unreadable != nil {
|
||||||
|
|||||||
@ -69,6 +69,19 @@ func (v *Variable) writeTo(buf *bytes.Buffer, top, newlines, includeType bool, i
|
|||||||
}
|
}
|
||||||
case reflect.Struct:
|
case reflect.Struct:
|
||||||
v.writeStructTo(buf, newlines, includeType, indent)
|
v.writeStructTo(buf, newlines, includeType, indent)
|
||||||
|
case reflect.Interface:
|
||||||
|
if includeType {
|
||||||
|
if v.Children[0].Kind == reflect.Invalid {
|
||||||
|
fmt.Fprintf(buf, "%s ", v.Type)
|
||||||
|
if v.Children[0].Addr == 0 {
|
||||||
|
fmt.Fprintf(buf, "nil")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(buf, "%s(%s) ", v.Type, v.Children[0].Type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
v.Children[0].writeTo(buf, false, newlines, false, indent)
|
||||||
case reflect.Map:
|
case reflect.Map:
|
||||||
v.writeMapTo(buf, newlines, includeType, indent)
|
v.writeMapTo(buf, newlines, includeType, indent)
|
||||||
case reflect.Func:
|
case reflect.Func:
|
||||||
@ -192,7 +205,7 @@ func (v *Variable) shouldNewlineArray(newlines bool) bool {
|
|||||||
kind, hasptr := (&v.Children[0]).recursiveKind()
|
kind, hasptr := (&v.Children[0]).recursiveKind()
|
||||||
|
|
||||||
switch kind {
|
switch kind {
|
||||||
case reflect.Slice, reflect.Array, reflect.Struct, reflect.Map:
|
case reflect.Slice, reflect.Array, reflect.Struct, reflect.Map, reflect.Interface:
|
||||||
return true
|
return true
|
||||||
case reflect.String:
|
case reflect.String:
|
||||||
if hasptr {
|
if hasptr {
|
||||||
@ -233,7 +246,7 @@ func (v *Variable) shouldNewlineStruct(newlines bool) bool {
|
|||||||
kind, hasptr := (&v.Children[i]).recursiveKind()
|
kind, hasptr := (&v.Children[i]).recursiveKind()
|
||||||
|
|
||||||
switch kind {
|
switch kind {
|
||||||
case reflect.Slice, reflect.Array, reflect.Struct, reflect.Map:
|
case reflect.Slice, reflect.Array, reflect.Struct, reflect.Map, reflect.Interface:
|
||||||
return true
|
return true
|
||||||
case reflect.String:
|
case reflect.String:
|
||||||
if hasptr {
|
if hasptr {
|
||||||
|
|||||||
@ -399,6 +399,20 @@ func TestEvalExpression(t *testing.T) {
|
|||||||
{"mnil[\"Malone\"]", false, "", "", "", fmt.Errorf("key not found")},
|
{"mnil[\"Malone\"]", false, "", "", "", fmt.Errorf("key not found")},
|
||||||
{"m1[80:]", false, "", "", "", fmt.Errorf("map index out of bounds")},
|
{"m1[80:]", false, "", "", "", fmt.Errorf("map index out of bounds")},
|
||||||
|
|
||||||
|
// interfaces
|
||||||
|
{"err1", true, "error(*struct 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},
|
||||||
|
{"iface1", true, "interface {}(*struct main.astruct) *{A: 1, B: 2}", "", "interface {}", nil},
|
||||||
|
{"ifacenil", true, "interface {} nil", "", "interface {}", nil},
|
||||||
|
{"err1 == err2", false, "false", "", "", nil},
|
||||||
|
{"err1 == iface1", false, "", "", "", fmt.Errorf("mismatched types \"error\" and \"interface {}\"")},
|
||||||
|
{"errnil == nil", false, "false", "", "", nil},
|
||||||
|
{"nil == errnil", false, "false", "", "", 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")},
|
||||||
|
{"errnil.(*main.astruct)", false, "", "", "", fmt.Errorf("interface conversion: error is nil, not *main.astruct")},
|
||||||
|
|
||||||
// combined expressions
|
// combined expressions
|
||||||
{"c1.pb.a.A", true, "1", "", "int", nil},
|
{"c1.pb.a.A", true, "1", "", "int", nil},
|
||||||
{"c1.sa[1].B", false, "3", "", "int", nil},
|
{"c1.sa[1].B", false, "3", "", "int", nil},
|
||||||
|
|||||||
Reference in New Issue
Block a user