proc: evaluate var.method expressions
Evaluates var.method expressions into a variable holding the corresponding method with the receiver variable as a child, in preparation for extending CallFunction so that it can call methods.
This commit is contained in:
parent
9335c54014
commit
51994aafd3
@ -62,6 +62,10 @@ func (a *astruct) Error() string {
|
|||||||
return "not an error"
|
return "not an error"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a astruct) NonPointerRecieverMethod() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func (b *bstruct) Error() string {
|
func (b *bstruct) Error() string {
|
||||||
return "not an error"
|
return "not an error"
|
||||||
}
|
}
|
||||||
@ -246,6 +250,8 @@ func main() {
|
|||||||
|
|
||||||
var nilstruct *astruct = nil
|
var nilstruct *astruct = nil
|
||||||
|
|
||||||
|
var as2 astruct
|
||||||
|
|
||||||
s4 := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}
|
s4 := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}
|
||||||
|
|
||||||
var amb1 = 1
|
var amb1 = 1
|
||||||
@ -255,5 +261,5 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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, anonstruct1, anonstruct2, anoniface1, anonfunc, mapanonstruct1, ifacearr, efacearr, ni8, ni16, ni32, pinf, ninf, nan, zsvmap, zsslice, zsvar, tm, errtypednil, emptyslice, emptymap, byteslice, runeslice, longstr, nilstruct, s4)
|
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, ifacearr, efacearr, ni8, ni16, ni32, pinf, ninf, nan, zsvmap, zsslice, zsvar, tm, errtypednil, emptyslice, emptymap, byteslice, runeslice, longstr, nilstruct, as2, as2.NonPointerRecieverMethod, s4)
|
||||||
}
|
}
|
||||||
|
152
pkg/proc/eval.go
152
pkg/proc/eval.go
@ -12,9 +12,11 @@ import (
|
|||||||
"go/token"
|
"go/token"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/derekparker/delve/pkg/dwarf/godwarf"
|
"github.com/derekparker/delve/pkg/dwarf/godwarf"
|
||||||
"github.com/derekparker/delve/pkg/dwarf/reader"
|
"github.com/derekparker/delve/pkg/dwarf/reader"
|
||||||
|
"github.com/derekparker/delve/pkg/goversion"
|
||||||
)
|
)
|
||||||
|
|
||||||
var OperationOnSpecialFloatError = errors.New("operations on non-finite floats not implemented")
|
var OperationOnSpecialFloatError = errors.New("operations on non-finite floats not implemented")
|
||||||
@ -599,6 +601,13 @@ func (scope *EvalScope) evalStructSelector(node *ast.SelectorExpr) (*Variable, e
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
rv, err := xv.findMethod(node.Sel.Name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if rv != nil {
|
||||||
|
return rv, nil
|
||||||
|
}
|
||||||
return xv.structMember(node.Sel.Name)
|
return xv.structMember(node.Sel.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -782,14 +791,18 @@ func (scope *EvalScope) evalAddrOf(node *ast.UnaryExpr) (*Variable, error) {
|
|||||||
return nil, fmt.Errorf("can not take address of \"%s\"", exprToString(node.X))
|
return nil, fmt.Errorf("can not take address of \"%s\"", exprToString(node.X))
|
||||||
}
|
}
|
||||||
|
|
||||||
xev.OnlyAddr = true
|
return xev.pointerToVariable(), nil
|
||||||
|
}
|
||||||
|
|
||||||
typename := "*" + xev.DwarfType.Common().Name
|
func (v *Variable) pointerToVariable() *Variable {
|
||||||
rv := scope.newVariable("", 0, &godwarf.PtrType{CommonType: godwarf.CommonType{ByteSize: int64(scope.BinInfo.Arch.PtrSize()), Name: typename}, Type: xev.DwarfType}, scope.Mem)
|
v.OnlyAddr = true
|
||||||
rv.Children = []Variable{*xev}
|
|
||||||
|
typename := "*" + v.DwarfType.Common().Name
|
||||||
|
rv := v.newVariable("", 0, &godwarf.PtrType{CommonType: godwarf.CommonType{ByteSize: int64(v.bi.Arch.PtrSize()), Name: typename}, Type: v.DwarfType}, v.mem)
|
||||||
|
rv.Children = []Variable{*v}
|
||||||
rv.loaded = true
|
rv.loaded = true
|
||||||
|
|
||||||
return rv, nil
|
return rv
|
||||||
}
|
}
|
||||||
|
|
||||||
func constantUnaryOp(op token.Token, y constant.Value) (r constant.Value, err error) {
|
func constantUnaryOp(op token.Token, y constant.Value) (r constant.Value, err error) {
|
||||||
@ -1304,6 +1317,77 @@ func (v *Variable) reslice(low int64, high int64) (*Variable, error) {
|
|||||||
return r, nil
|
return r, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// findMethod finds method mname in the type of variable v
|
||||||
|
func (v *Variable) findMethod(mname string) (*Variable, error) {
|
||||||
|
if _, isiface := v.RealType.(*godwarf.InterfaceType); isiface {
|
||||||
|
v.loadInterface(0, false, loadFullValue)
|
||||||
|
if v.Unreadable != nil {
|
||||||
|
return nil, v.Unreadable
|
||||||
|
}
|
||||||
|
return v.Children[0].findMethod(mname)
|
||||||
|
}
|
||||||
|
|
||||||
|
typ := v.DwarfType
|
||||||
|
ptyp, isptr := typ.(*godwarf.PtrType)
|
||||||
|
if isptr {
|
||||||
|
typ = ptyp.Type
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, istypedef := typ.(*godwarf.TypedefType); !istypedef {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
typePath := typ.Common().Name
|
||||||
|
dot := strings.LastIndex(typePath, ".")
|
||||||
|
if dot < 0 {
|
||||||
|
// probably just a C type
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
pkg := typePath[:dot]
|
||||||
|
receiver := typePath[dot+1:]
|
||||||
|
|
||||||
|
if fn, ok := v.bi.LookupFunc[fmt.Sprintf("%s.%s.%s", pkg, receiver, mname)]; ok {
|
||||||
|
r, err := functionToVariable(fn, v.bi, v.mem)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if isptr {
|
||||||
|
r.Children = append(r.Children, *(v.maybeDereference()))
|
||||||
|
} else {
|
||||||
|
r.Children = append(r.Children, *v)
|
||||||
|
}
|
||||||
|
return r, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if fn, ok := v.bi.LookupFunc[fmt.Sprintf("%s.(*%s).%s", pkg, receiver, mname)]; ok {
|
||||||
|
r, err := functionToVariable(fn, v.bi, v.mem)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if isptr {
|
||||||
|
r.Children = append(r.Children, *v)
|
||||||
|
} else {
|
||||||
|
r.Children = append(r.Children, *(v.pointerToVariable()))
|
||||||
|
}
|
||||||
|
return r, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func functionToVariable(fn *Function, bi *BinaryInfo, mem MemoryReadWriter) (*Variable, error) {
|
||||||
|
typ, err := fn.fakeType(bi, true)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
v := newVariable(fn.Name, 0, typ, bi, mem)
|
||||||
|
v.Value = constant.MakeString(fn.Name)
|
||||||
|
v.loaded = true
|
||||||
|
v.Base = uintptr(fn.Entry)
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
|
||||||
func fakeSliceType(fieldType godwarf.Type) godwarf.Type {
|
func fakeSliceType(fieldType godwarf.Type) godwarf.Type {
|
||||||
return &godwarf.SliceType{
|
return &godwarf.SliceType{
|
||||||
StructType: godwarf.StructType{
|
StructType: godwarf.StructType{
|
||||||
@ -1318,3 +1402,61 @@ func fakeSliceType(fieldType godwarf.Type) godwarf.Type {
|
|||||||
ElemType: fieldType,
|
ElemType: fieldType,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var errMethodEvalUnsupported = errors.New("evaluating methods not supported on this version of Go")
|
||||||
|
|
||||||
|
func (fn *Function) fakeType(bi *BinaryInfo, removeReceiver bool) (*godwarf.FuncType, error) {
|
||||||
|
if producer := bi.Producer(); producer == "" || !goversion.ProducerAfterOrEqual(producer, 1, 10) {
|
||||||
|
// versions of Go prior to 1.10 do not distinguish between parameters and
|
||||||
|
// return values, therefore we can't use a subprogram DIE to derive a
|
||||||
|
// function type.
|
||||||
|
return nil, errMethodEvalUnsupported
|
||||||
|
}
|
||||||
|
_, formalArgs, err := funcCallArgs(fn, bi, true)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if removeReceiver {
|
||||||
|
formalArgs = formalArgs[1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
args := make([]string, 0, len(formalArgs))
|
||||||
|
rets := make([]string, 0, len(formalArgs))
|
||||||
|
|
||||||
|
for _, formalArg := range formalArgs {
|
||||||
|
var s string
|
||||||
|
if strings.HasPrefix(formalArg.name, "~") {
|
||||||
|
s = formalArg.typ.String()
|
||||||
|
} else {
|
||||||
|
s = fmt.Sprintf("%s %s", formalArg.name, formalArg.typ.String())
|
||||||
|
}
|
||||||
|
if formalArg.isret {
|
||||||
|
rets = append(rets, s)
|
||||||
|
} else {
|
||||||
|
args = append(args, s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
argstr := strings.Join(args, ", ")
|
||||||
|
var retstr string
|
||||||
|
switch len(rets) {
|
||||||
|
case 0:
|
||||||
|
retstr = ""
|
||||||
|
case 1:
|
||||||
|
retstr = " " + rets[0]
|
||||||
|
default:
|
||||||
|
retstr = " (" + strings.Join(rets, ", ") + ")"
|
||||||
|
}
|
||||||
|
return &godwarf.FuncType{
|
||||||
|
CommonType: godwarf.CommonType{
|
||||||
|
Name: "func(" + argstr + ")" + retstr,
|
||||||
|
ReflectKind: reflect.Func,
|
||||||
|
},
|
||||||
|
//TODO(aarzilli): at the moment we aren't using the ParamType and
|
||||||
|
// ReturnType fields of FuncType anywhere (when this is returned to the
|
||||||
|
// client it's first converted to a string and the function calling code
|
||||||
|
// reads the subroutine entry because it needs to know the stack offsets).
|
||||||
|
// If we start using them they should be filled here.
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
@ -239,49 +239,16 @@ type funcCallArg struct {
|
|||||||
name string
|
name string
|
||||||
typ godwarf.Type
|
typ godwarf.Type
|
||||||
off int64
|
off int64
|
||||||
|
isret bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// funcCallArgFrame checks type and pointer escaping for the arguments and
|
// funcCallArgFrame checks type and pointer escaping for the arguments and
|
||||||
// returns the argument frame.
|
// returns the argument frame.
|
||||||
func funcCallArgFrame(fn *Function, actualArgs []*Variable, g *G, bi *BinaryInfo) (argmem []byte, err error) {
|
func funcCallArgFrame(fn *Function, actualArgs []*Variable, g *G, bi *BinaryInfo) (argmem []byte, err error) {
|
||||||
const CFA = 0x1000
|
argFrameSize, formalArgs, err := funcCallArgs(fn, bi, false)
|
||||||
vrdr := reader.Variables(bi.dwarf, fn.offset, fn.Entry, int(^uint(0)>>1), false)
|
|
||||||
formalArgs := []funcCallArg{}
|
|
||||||
|
|
||||||
// typechecks arguments, calculates argument frame size
|
|
||||||
argFrameSize := int64(0)
|
|
||||||
for vrdr.Next() {
|
|
||||||
e := vrdr.Entry()
|
|
||||||
if e.Tag != dwarf.TagFormalParameter {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
entry, argname, typ, err := readVarEntry(e, bi)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
typ = resolveTypedef(typ)
|
|
||||||
locprog, _, err := bi.locationExpr(entry, dwarf.AttrLocation, fn.Entry)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("could not get argument location of %s: %v", argname, err)
|
|
||||||
}
|
|
||||||
off, _, err := op.ExecuteStackProgram(op.DwarfRegisters{CFA: CFA, FrameBase: CFA}, locprog)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("unsupported location expression for argument %s: %v", argname, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
off -= CFA
|
|
||||||
|
|
||||||
if e := off + typ.Size(); e > argFrameSize {
|
|
||||||
argFrameSize = e
|
|
||||||
}
|
|
||||||
|
|
||||||
if isret, _ := entry.Val(dwarf.AttrVarParam).(bool); !isret {
|
|
||||||
formalArgs = append(formalArgs, funcCallArg{name: argname, typ: typ, off: off})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err := vrdr.Err(); err != nil {
|
|
||||||
return nil, fmt.Errorf("DWARF read error: %v", err)
|
|
||||||
}
|
|
||||||
if len(actualArgs) > len(formalArgs) {
|
if len(actualArgs) > len(formalArgs) {
|
||||||
return nil, ErrTooManyArguments
|
return nil, ErrTooManyArguments
|
||||||
}
|
}
|
||||||
@ -289,10 +256,6 @@ func funcCallArgFrame(fn *Function, actualArgs []*Variable, g *G, bi *BinaryInfo
|
|||||||
return nil, ErrNotEnoughArguments
|
return nil, ErrNotEnoughArguments
|
||||||
}
|
}
|
||||||
|
|
||||||
sort.Slice(formalArgs, func(i, j int) bool {
|
|
||||||
return formalArgs[i].off < formalArgs[j].off
|
|
||||||
})
|
|
||||||
|
|
||||||
// constructs arguments frame
|
// constructs arguments frame
|
||||||
argmem = make([]byte, argFrameSize)
|
argmem = make([]byte, argFrameSize)
|
||||||
argmemWriter := &bufferMemoryReadWriter{argmem}
|
argmemWriter := &bufferMemoryReadWriter{argmem}
|
||||||
@ -317,6 +280,51 @@ func funcCallArgFrame(fn *Function, actualArgs []*Variable, g *G, bi *BinaryInfo
|
|||||||
return argmem, nil
|
return argmem, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func funcCallArgs(fn *Function, bi *BinaryInfo, includeRet bool) (argFrameSize int64, formalArgs []funcCallArg, err error) {
|
||||||
|
const CFA = 0x1000
|
||||||
|
vrdr := reader.Variables(bi.dwarf, fn.offset, fn.Entry, int(^uint(0)>>1), false)
|
||||||
|
|
||||||
|
// typechecks arguments, calculates argument frame size
|
||||||
|
for vrdr.Next() {
|
||||||
|
e := vrdr.Entry()
|
||||||
|
if e.Tag != dwarf.TagFormalParameter {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
entry, argname, typ, err := readVarEntry(e, bi)
|
||||||
|
if err != nil {
|
||||||
|
return 0, nil, err
|
||||||
|
}
|
||||||
|
typ = resolveTypedef(typ)
|
||||||
|
locprog, _, err := bi.locationExpr(entry, dwarf.AttrLocation, fn.Entry)
|
||||||
|
if err != nil {
|
||||||
|
return 0, nil, fmt.Errorf("could not get argument location of %s: %v", argname, err)
|
||||||
|
}
|
||||||
|
off, _, err := op.ExecuteStackProgram(op.DwarfRegisters{CFA: CFA, FrameBase: CFA}, locprog)
|
||||||
|
if err != nil {
|
||||||
|
return 0, nil, fmt.Errorf("unsupported location expression for argument %s: %v", argname, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
off -= CFA
|
||||||
|
|
||||||
|
if e := off + typ.Size(); e > argFrameSize {
|
||||||
|
argFrameSize = e
|
||||||
|
}
|
||||||
|
|
||||||
|
if isret, _ := entry.Val(dwarf.AttrVarParam).(bool); !isret || includeRet {
|
||||||
|
formalArgs = append(formalArgs, funcCallArg{name: argname, typ: typ, off: off, isret: isret})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := vrdr.Err(); err != nil {
|
||||||
|
return 0, nil, fmt.Errorf("DWARF read error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Slice(formalArgs, func(i, j int) bool {
|
||||||
|
return formalArgs[i].off < formalArgs[j].off
|
||||||
|
})
|
||||||
|
|
||||||
|
return argFrameSize, formalArgs, nil
|
||||||
|
}
|
||||||
|
|
||||||
func escapeCheck(v *Variable, name string, g *G) error {
|
func escapeCheck(v *Variable, name string, g *G) error {
|
||||||
switch v.Kind {
|
switch v.Kind {
|
||||||
case reflect.Ptr:
|
case reflect.Ptr:
|
||||||
|
@ -791,6 +791,11 @@ func TestEvalExpression(t *testing.T) {
|
|||||||
|
|
||||||
{"afunc", true, `main.afunc`, `main.afunc`, `func()`, nil},
|
{"afunc", true, `main.afunc`, `main.afunc`, `func()`, nil},
|
||||||
{"main.afunc2", true, `main.afunc2`, `main.afunc2`, `func()`, nil},
|
{"main.afunc2", true, `main.afunc2`, `main.afunc2`, `func()`, nil},
|
||||||
|
|
||||||
|
{"s2[0].Error", false, "main.(*astruct).Error", "main.(*astruct).Error", "func() string", nil},
|
||||||
|
{"s2[0].NonPointerRecieverMethod", false, "main.astruct.NonPointerRecieverMethod", "main.astruct.NonPointerRecieverMethod", "func()", nil},
|
||||||
|
{"as2.Error", false, "main.(*astruct).Error", "main.(*astruct).Error", "func() string", nil},
|
||||||
|
{"as2.NonPointerRecieverMethod", false, "main.astruct.NonPointerRecieverMethod", "main.astruct.NonPointerRecieverMethod", "func()", nil},
|
||||||
}
|
}
|
||||||
|
|
||||||
ver, _ := goversion.Parse(runtime.Version())
|
ver, _ := goversion.Parse(runtime.Version())
|
||||||
@ -808,6 +813,10 @@ func TestEvalExpression(t *testing.T) {
|
|||||||
assertNoError(proc.Continue(p), t, "Continue() returned an error")
|
assertNoError(proc.Continue(p), t, "Continue() returned an error")
|
||||||
for _, tc := range testcases {
|
for _, tc := range testcases {
|
||||||
variable, err := evalVariable(p, tc.name, pnormalLoadConfig)
|
variable, err := evalVariable(p, tc.name, pnormalLoadConfig)
|
||||||
|
if err != nil && err.Error() == "evaluating methods not supported on this version of Go" {
|
||||||
|
// this type of eval is unsupported with the current version of Go.
|
||||||
|
continue
|
||||||
|
}
|
||||||
if tc.err == nil {
|
if tc.err == nil {
|
||||||
assertNoError(err, t, fmt.Sprintf("EvalExpression(%s) returned an error", tc.name))
|
assertNoError(err, t, fmt.Sprintf("EvalExpression(%s) returned an error", tc.name))
|
||||||
assertVariable(t, variable, tc)
|
assertVariable(t, variable, tc)
|
||||||
|
Loading…
Reference in New Issue
Block a user