提交 7c42fc51 编写于 作者: A aarzilli 提交者: Derek Parker

proc: support calls to methods directly and through interface

上级 51994aaf
......@@ -35,6 +35,26 @@ func stringsJoin(v []string, sep string) string {
return strings.Join(v, sep)
}
type astruct struct {
X int
}
func (a astruct) VRcvr(x int) string {
return fmt.Sprintf("%d + %d = %d", x, a.X, x+a.X)
}
func (pa *astruct) PRcvr(x int) string {
return fmt.Sprintf("%d - %d = %d", x, pa.X, x-pa.X)
}
type PRcvrable interface {
PRcvr(int) string
}
type VRcvrable interface {
VRcvr(int) string
}
var zero = 0
func main() {
......@@ -42,7 +62,14 @@ func main() {
intslice := []int{1, 2, 3}
stringslice := []string{"one", "two", "three"}
comma := ","
a := astruct{X: 3}
pa := &astruct{X: 6}
var vable_a VRcvrable = a
var vable_pa VRcvrable = pa
var pable_pa PRcvrable = pa
runtime.Breakpoint()
call1(one, two)
fmt.Println(one, two, zero, callpanic, callstacktrace, stringsJoin, intslice, stringslice, comma)
fmt.Println(one, two, zero, callpanic, callstacktrace, stringsJoin, intslice, stringslice, comma, a.VRcvr, a.PRcvr, pa, vable_a, vable_pa, pable_pa)
}
......@@ -207,7 +207,6 @@ func funcCallEvalExpr(p Process, expr string) (fn *Function, argvars []*Variable
return nil, nil, ErrNotACallExpr
}
//TODO(aarzilli): must evaluate <var>.<method> and treat them appropriately
fnvar, err := scope.evalAST(callexpr.Fun)
if err != nil {
return nil, nil, err
......@@ -223,13 +222,18 @@ func funcCallEvalExpr(p Process, expr string) (fn *Function, argvars []*Variable
return nil, nil, ErrNotAGoFunction
}
argvars = make([]*Variable, len(callexpr.Args))
argvars = make([]*Variable, 0, len(callexpr.Args)+1)
if len(fnvar.Children) > 0 {
// receiver argument
argvars = append(argvars, &fnvar.Children[0])
}
for i := range callexpr.Args {
argvars[i], err = scope.evalAST(callexpr.Args[i])
argvar, err := scope.evalAST(callexpr.Args[i])
if err != nil {
return nil, nil, err
}
argvars[i].Name = exprToString(callexpr.Args[i])
argvar.Name = exprToString(callexpr.Args[i])
argvars = append(argvars, argvar)
}
return fn, argvars, nil
......
......@@ -192,6 +192,7 @@ func (bi *BinaryInfo) loadDebugInfoMaps(debugLineBytes []byte, wg *sync.WaitGrou
bi.consts = make(map[dwarf.Offset]*constantType)
bi.runtimeTypeToDIE = make(map[uint64]runtimeTypeDIE)
reader := bi.DwarfReader()
ardr := bi.DwarfReader()
var cu *compileUnit = nil
var pu *partialUnit = nil
var partialUnits = make(map[dwarf.Offset]*partialUnit)
......@@ -322,9 +323,7 @@ outer:
}
}
}
if off, ok := entry.Val(godwarf.AttrGoRuntimeType).(uint64); ok {
bi.runtimeTypeToDIE[off] = runtimeTypeDIE{entry.Offset, -1}
}
bi.registerRuntimeTypeToDIE(entry, ardr)
reader.SkipChildren()
case dwarf.TagVariable:
......@@ -516,6 +515,37 @@ func (bi *BinaryInfo) expandPackagesInType(expr ast.Expr) {
}
}
func (bi *BinaryInfo) registerRuntimeTypeToDIE(entry *dwarf.Entry, ardr *reader.Reader) {
if off, ok := entry.Val(godwarf.AttrGoRuntimeType).(uint64); ok {
if _, ok := bi.runtimeTypeToDIE[off]; !ok {
bi.runtimeTypeToDIE[off] = runtimeTypeDIE{entry.Offset, -1}
}
return
}
if entry.Tag != dwarf.TagTypedef {
return
}
// For named structs the compiler will emit a TagStructType entry and a
// TagTypedef entry. The AttrGoRuntimeType is set on the TagStructType
// entry but we prefer to use the typedef instead, to make interface
// values consistent with other variables.
rtypOff, ok := entry.Val(dwarf.AttrType).(dwarf.Offset)
if !ok {
return
}
ardr.Seek(rtypOff)
rentry, _ := ardr.Next()
if rentry == nil {
return
}
if off, ok := rentry.Val(godwarf.AttrGoRuntimeType).(uint64); ok {
bi.runtimeTypeToDIE[off] = runtimeTypeDIE{entry.Offset, -1}
}
}
// runtimeTypeToDIE returns the DIE corresponding to the runtime._type.
// This is done in three different ways depending on the version of go.
// * Before go1.7 the type name is retrieved directly from the runtime._type
......
......@@ -733,6 +733,7 @@ func (v *Variable) structMember(memberName string) (*Variable, error) {
if v.Unreadable != nil {
return v.clone(), nil
}
vname := v.Name
switch v.Kind {
case reflect.Chan:
v = v.clone()
......@@ -789,12 +790,12 @@ func (v *Variable) structMember(memberName string) (*Variable, error) {
return embeddedField, nil
}
}
return nil, fmt.Errorf("%s has no member %s", v.Name, memberName)
return nil, fmt.Errorf("%s has no member %s", vname, memberName)
default:
if v.Name == "" {
return nil, fmt.Errorf("type %s is not a struct", structVar.TypeString())
}
return nil, fmt.Errorf("%s (type %s) is not a struct", v.Name, structVar.TypeString())
return nil, fmt.Errorf("%s (type %s) is not a struct", vname, structVar.TypeString())
}
}
......
......@@ -1087,6 +1087,28 @@ func TestCallFunction(t *testing.T) {
{`stringsJoin(stringslice, comma)`, []string{`:string:"one,two,three"`}, nil},
{`stringsJoin(s1, comma)`, nil, errors.New("could not find symbol value for s1")},
{`stringsJoin(intslice, comma)`, nil, errors.New("can not convert value of type []int to []string")},
// The following set of calls was constructed using https://docs.google.com/document/d/1bMwCey-gmqZVTpRax-ESeVuZGmjwbocYs1iHplK-cjo/pub as a reference
{`a.VRcvr(1)`, []string{`:string:"1 + 3 = 4"`}, nil}, // direct call of a method with value receiver / on a value
{`a.PRcvr(2)`, []string{`:string:"2 - 3 = -1"`}, nil}, // direct call of a method with pointer receiver / on a value
{`pa.VRcvr(3)`, []string{`:string:"3 + 6 = 9"`}, nil}, // direct call of a method with value receiver / on a pointer
{`pa.PRcvr(4)`, []string{`:string:"4 - 6 = -2"`}, nil}, // direct call of a method with pointer receiver / on a pointer
{`vable_pa.VRcvr(6)`, []string{`:string:"6 + 6 = 12"`}, nil}, // indirect call of method on interface / containing value with value method
{`pable_pa.PRcvr(7)`, []string{`:string:"7 - 6 = 1"`}, nil}, // indirect call of method on interface / containing pointer with value method
{`vable_a.VRcvr(5)`, []string{`:string:"5 + 3 = 8"`}, nil}, // indirect call of method on interface / containing pointer with pointer method
{`pa.nonexistent()`, nil, errors.New("pa has no member nonexistent")},
{`a.nonexistent()`, nil, errors.New("a has no member nonexistent")},
{`vable_pa.nonexistent()`, nil, errors.New("vable_pa has no member nonexistent")},
{`vable_a.nonexistent()`, nil, errors.New("vable_a has no member nonexistent")},
{`pable_pa.nonexistent()`, nil, errors.New("pable_pa has no member nonexistent")},
//TODO(aarzilli): indirect call of func value / set to top-level func
//TODO(aarzilli): indirect call of func value / set to func literal
//TODO(aarzilli): indirect call of func value / set to value method
//TODO(aarzilli): indirect call of func value / set to pointer method
}
withTestProcess("fncall", t, func(p proc.Process, fixture protest.Fixture) {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册