proc: allow use of quoted type names in type casts (#434)

If we are unable to correctly guess how a type is serialized in
debug_info let the user specify it directly.

Workaround for #455
This commit is contained in:
Alessandro Arzilli 2016-04-25 01:14:42 +02:00 committed by Derek Parker
parent 7820921b7e
commit a7a0cc75e1
5 changed files with 61 additions and 5 deletions

@ -187,6 +187,16 @@ func main() {
b2 := B{A: A{42}, a: A{47}} b2 := B{A: A{42}, a: A{47}}
var sd D var sd D
var mapanonstruct1 map[string]struct{}
var anonstruct1 struct{ val constant.Value }
var anonstruct2 struct{ i, j int }
var anoniface1 interface {
SomeFunction(struct{ val constant.Value })
OtherFunction(i, j int)
}
var anonfunc func(a struct{ i int }, b interface{}, c struct{ val constant.Value })
for i := range benchparr { for i := range benchparr {
benchparr[i] = &benchstruct{} benchparr[i] = &benchstruct{}
} }
@ -197,5 +207,5 @@ func main() {
fmt.Println(amb1) fmt.Println(amb1)
} }
runtime.Breakpoint() 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, recursive1, recursive1.x, iface5, iface2fn1, iface2fn2, bencharr, benchparr, mapinf, mainMenu, b, b2, sd) 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, recursive1, recursive1.x, iface5, iface2fn1, iface2fn2, bencharr, benchparr, mapinf, mainMenu, b, b2, sd, anonstruct1, anonstruct2, anoniface1, anonfunc, mapanonstruct1)
} }

@ -104,7 +104,7 @@ func (reader *Reader) AddrForMember(member string, initialInstructions []byte) (
} }
} }
var TypeNotFoundErr = errors.New("no type entry found") var TypeNotFoundErr = errors.New("no type entry found, use 'types' for a list of valid types")
// SeekToType moves the reader to the type specified by the entry, // SeekToType moves the reader to the type specified by the entry,
// optionally resolving typedefs and pointer types. If the reader is set // optionally resolving typedefs and pointer types. If the reader is set

@ -41,7 +41,13 @@ func (scope *EvalScope) evalAST(t ast.Expr) (*Variable, error) {
if err == nil { if err == nil {
return v, nil return v, nil
} }
if err != reader.TypeNotFoundErr { _, isident := node.Fun.(*ast.Ident)
// we don't support function calls at the moment except for a few
// builtin functions so just return the type error here if the function
// isn't an identifier.
// More sophisticated logic will be required when function calls
// are implemented.
if err != reader.TypeNotFoundErr || !isident {
return v, err return v, err
} }
} }

@ -3,8 +3,10 @@ package proc
import ( import (
"github.com/derekparker/delve/dwarf/reader" "github.com/derekparker/delve/dwarf/reader"
"go/ast" "go/ast"
"go/token"
"golang.org/x/debug/dwarf" "golang.org/x/debug/dwarf"
"reflect" "reflect"
"strconv"
"strings" "strings"
"sync" "sync"
) )
@ -24,13 +26,20 @@ func (dbp *Process) pointerTo(typ dwarf.Type) dwarf.Type {
func (dbp *Process) findTypeExpr(expr ast.Expr) (dwarf.Type, error) { func (dbp *Process) findTypeExpr(expr ast.Expr) (dwarf.Type, error) {
dbp.loadPackageMap() dbp.loadPackageMap()
if lit, islit := expr.(*ast.BasicLit); islit && lit.Kind == token.STRING {
// Allow users to specify type names verbatim as quoted
// string. Useful as a catch-all workaround for cases where we don't
// parse/serialize types correctly or can not resolve package paths.
typn, _ := strconv.Unquote(lit.Value)
return dbp.findType(typn)
}
dbp.expandPackagesInType(expr) dbp.expandPackagesInType(expr)
if snode, ok := expr.(*ast.StarExpr); ok { if snode, ok := expr.(*ast.StarExpr); ok {
// Pointer types only appear in the dwarf informations when // Pointer types only appear in the dwarf informations when
// a pointer to the type is used in the target program, here // 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 // we create a pointer type on the fly so that the user can
// specify a pointer to any variable used in the target program // specify a pointer to any variable used in the target program
ptyp, err := dbp.findType(exprToString(snode.X)) ptyp, err := dbp.findTypeExpr(snode.X)
if err != nil { if err != nil {
return nil, err return nil, err
} }

@ -598,7 +598,7 @@ func TestEvalExpression(t *testing.T) {
{"i2 << i3", false, "", "", "int", fmt.Errorf("shift count type int, must be unsigned integer")}, {"i2 << i3", false, "", "", "int", fmt.Errorf("shift count type int, must be unsigned integer")},
{"*(i2 + i3)", false, "", "", "", fmt.Errorf("expression \"(i2 + i3)\" (int) can not be dereferenced")}, {"*(i2 + i3)", false, "", "", "", fmt.Errorf("expression \"(i2 + i3)\" (int) can not be dereferenced")},
{"i2.member", false, "", "", "", fmt.Errorf("i2 (type int) is not a struct")}, {"i2.member", false, "", "", "", fmt.Errorf("i2 (type int) is not a struct")},
{"fmt.Println(\"hello\")", false, "", "", "", fmt.Errorf("function calls are not supported")}, {"fmt.Println(\"hello\")", false, "", "", "", fmt.Errorf("no type entry found, use 'types' for a list of valid types")},
{"*nil", false, "", "", "", fmt.Errorf("nil can not be dereferenced")}, {"*nil", false, "", "", "", fmt.Errorf("nil can not be dereferenced")},
{"!nil", false, "", "", "", fmt.Errorf("operator ! can not be applied to \"nil\"")}, {"!nil", false, "", "", "", fmt.Errorf("operator ! can not be applied to \"nil\"")},
{"&nil", false, "", "", "", fmt.Errorf("can not take address of \"nil\"")}, {"&nil", false, "", "", "", fmt.Errorf("can not take address of \"nil\"")},
@ -718,3 +718,34 @@ func TestUnsafePointer(t *testing.T) {
} }
}) })
} }
type issue426TestCase struct {
name string
typ string
}
func TestIssue426(t *testing.T) {
// type casts using quoted type names
testcases := []issue426TestCase{
{"iface1", `interface {}`},
{"mapanonstruct1", `map[string]struct {}`},
{"anonstruct1", `struct { val go/constant.Value }`},
{"anonfunc", `func(struct { i int }, interface {}, struct { val go/constant.Value })`},
{"anonstruct2", `struct { i int; j int }`},
{"anoniface1", `interface { OtherFunction(int, int); SomeFunction(struct { val go/constant.Value }) }`},
}
// Serialization of type expressions (go/ast.Expr) containing anonymous structs or interfaces
// differs from the serialization used by the linker to produce DWARF type information
withTestProcess("testvariables2", t, func(p *proc.Process, fixture protest.Fixture) {
assertNoError(p.Continue(), t, "Continue() returned an error")
for _, testcase := range testcases {
v, err := evalVariable(p, testcase.name, pnormalLoadConfig)
assertNoError(err, t, fmt.Sprintf("EvalVariable(%s)", testcase.name))
t.Logf("%s → %s", testcase.name, v.RealType.String())
expr := fmt.Sprintf("(*%q)(%d)", testcase.typ, v.Addr)
_, err = evalVariable(p, expr, pnormalLoadConfig)
assertNoError(err, t, fmt.Sprintf("EvalVariable(%s)", expr))
}
})
}