proc: add iface.(data) syntax to access concrete value of an interface

With this syntax users do not need to type the concrete type of an
interface variable to access its contents. This also sidesteps the
problem where the serialization of a type by go/printer is different
from the one used for debug_info type names.

Updates #1328
This commit is contained in:
aarzilli 2018-09-14 13:52:41 +02:00 committed by Derek Parker
parent 1b4c318376
commit 79a0e216ab
4 changed files with 38 additions and 8 deletions

@ -75,13 +75,27 @@ interface {}(*struct string) *"test"
error(*struct main.astruct) *{A: 1, B: 2} error(*struct main.astruct) *{A: 1, B: 2}
``` ```
To use a field of a struct contained inside an interface variable use a type assertion: To use the contents of an interface variable use a type assertion:
``` ```
(dlv) p iface1.(*main.astruct).B (dlv) p iface1.(*main.astruct).B
2 2
``` ```
Or just use the special `.(data)` type assertion:
```
(dlv) p iface1.(data).B
2
```
If the contents of the interface variable are a struct or a pointer to struct the fields can also be accessed directly:
```
(dlv) p iface1.B
2
```
# Specifying package paths # Specifying package paths
Packages with the same name can be disambiguated by using the full package path. For example, if the application imports two packages, `some/package` and `some/other/package`, both defining a variable `A`, the two variables can be accessed using this syntax: Packages with the same name can be disambiguated by using the full package path. For example, if the application imports two packages, `some/package` and `some/other/package`, both defining a variable `A`, the two variables can be accessed using this syntax:

@ -254,6 +254,15 @@ func main() {
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 iface2map interface{} = map[string]interface{}{
"a": map[string]interface{}{
"1": map[string]interface{}{
"x": 1,
"y": 2,
},
},
}
var amb1 = 1 var amb1 = 1
runtime.Breakpoint() runtime.Breakpoint()
for amb1 := 0; amb1 < 10; amb1++ { for amb1 := 0; amb1 < 10; amb1++ {
@ -261,5 +270,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, as2, as2.NonPointerRecieverMethod, 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, iface2map)
} }

@ -640,12 +640,17 @@ func (scope *EvalScope) evalTypeAssert(node *ast.TypeAssertExpr) (*Variable, err
if xv.Children[0].Addr == 0 { if xv.Children[0].Addr == 0 {
return nil, fmt.Errorf("interface conversion: %s is nil, not %s", xv.DwarfType.String(), exprToString(node.Type)) return nil, fmt.Errorf("interface conversion: %s is nil, not %s", xv.DwarfType.String(), exprToString(node.Type))
} }
typ, err := scope.BinInfo.findTypeExpr(node.Type) // Accept .(data) as a type assertion that always succeeds, so that users
if err != nil { // can access the data field of an interface without actually having to
return nil, err // type the concrete type.
} if idtyp, isident := node.Type.(*ast.Ident); !isident || idtyp.Name != "data" {
if xv.Children[0].DwarfType.Common().Name != typ.Common().Name { typ, err := scope.BinInfo.findTypeExpr(node.Type)
return nil, fmt.Errorf("interface conversion: %s is %s, not %s", xv.DwarfType.Common().Name, xv.Children[0].TypeString(), typ.Common().Name) if err != nil {
return nil, err
}
if xv.Children[0].DwarfType.Common().Name != typ.Common().Name {
return nil, fmt.Errorf("interface conversion: %s is %s, not %s", xv.DwarfType.Common().Name, xv.Children[0].TypeString(), typ.Common().Name)
}
} }
// loadInterface will set OnlyAddr for the data member since here we are // loadInterface will set OnlyAddr for the data member since here we are
// passing false to loadData, however returning the variable with OnlyAddr // passing false to loadData, however returning the variable with OnlyAddr

@ -798,6 +798,8 @@ func TestEvalExpression(t *testing.T) {
{"s2[0].NonPointerRecieverMethod", false, "main.astruct.NonPointerRecieverMethod", "main.astruct.NonPointerRecieverMethod", "func()", 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.Error", false, "main.(*astruct).Error", "main.(*astruct).Error", "func() string", nil},
{"as2.NonPointerRecieverMethod", false, "main.astruct.NonPointerRecieverMethod", "main.astruct.NonPointerRecieverMethod", "func()", nil}, {"as2.NonPointerRecieverMethod", false, "main.astruct.NonPointerRecieverMethod", "main.astruct.NonPointerRecieverMethod", "func()", nil},
{`iface2map.(data)`, false, "…", "…", "map[string]interface {}", nil},
} }
ver, _ := goversion.Parse(runtime.Version()) ver, _ := goversion.Parse(runtime.Version())