提交 9bc6ad4f 编写于 作者: A Alessandro Arzilli 提交者: Derek Parker

Go 1.7 compatibility (#524)

* tests: update to cope with go1.7 SSA compiler

* de-vendored golang.org/x/debug/dwarf

We need our own tweaked version

* dwarf/debug/dwarf: always use the entry's name attribute

Using the name attribute leads to better type names as well as fixes
inconsistencies between 1.5, 1.6 and 1.7.

* proc: Updated loadInterface to work with go1.7

go1.7 changed the internal representation of types, removing the string
field from runtime._type.
Updated loadInterface to use the new str field.
上级 5933a0f4
......@@ -69,18 +69,6 @@
"ImportPath": "github.com/spf13/pflag",
"Rev": "b084184666e02084b8ccb9b704bf0d79c466eb1d"
},
{
"ImportPath": "golang.org/x/debug/dwarf",
"Rev": "450bc3a73495e77763c92c336e3bc040b3f34c14"
},
{
"ImportPath": "golang.org/x/debug/elf",
"Rev": "450bc3a73495e77763c92c336e3bc040b3f34c14"
},
{
"ImportPath": "golang.org/x/debug/macho",
"Rev": "450bc3a73495e77763c92c336e3bc040b3f34c14"
},
{
"ImportPath": "golang.org/x/sys/unix",
"Rev": "eb2c74142fd19a79b3f237334c7384d5167b1b46"
......
......@@ -30,7 +30,8 @@ func anotherFunction() {
func main() {
var a SomeType
var b OtherType
fmt.Printf("%s %s\n", a.String(), b.String())
i := 10
fmt.Printf("%s %s %v\n", a.String(), b.String(), i)
a.SomeFunction()
anotherFunction()
ioutil.ReadFile("nonexistent.file.txt")
......
......@@ -5,6 +5,8 @@ import "math"
var f = 1.5
func main() {
_ = math.Floor(f)
_ = float64(int(f))
floatvar1 := math.Floor(f)
floatvar2 := float64(int(f))
_ = floatvar1
_ = floatvar2
}
......@@ -138,7 +138,12 @@ type PtrType struct {
Type Type
}
func (t *PtrType) String() string { return "*" + t.Type.String() }
func (t *PtrType) String() string {
if t.Name != "" {
return t.Name
}
return "*" + t.Type.String()
}
// A StructType represents a struct, union, or C++ class type.
type StructType struct {
......@@ -160,6 +165,9 @@ type StructField struct {
}
func (t *StructType) String() string {
if t.Name != "" {
return t.Name
}
if t.StructName != "" {
return t.Kind + " " + t.StructName
}
......@@ -591,6 +599,7 @@ func (d *Data) readType(name string, r typeReader, off Offset, typeCache map[Off
case TagUnionType:
t.Kind = "union"
}
t.Name, _ = e.Val(AttrName).(string)
t.StructName, _ = e.Val(AttrName).(string)
t.Incomplete = e.Val(AttrDeclaration) != nil
t.Field = make([]*StructField, 0, 8)
......@@ -705,6 +714,7 @@ func (d *Data) readType(name string, r typeReader, off Offset, typeCache map[Off
// AttrType: subtype [not required! void* has no AttrType]
// AttrAddrClass: address class [ignored]
t := new(PtrType)
t.Name, _ = e.Val(AttrName).(string)
t.ReflectKind = getKind(e)
typ = t
typeCache[off] = t
......
......@@ -13,7 +13,7 @@ import (
"io"
"os"
"golang.org/x/debug/dwarf"
"github.com/derekparker/delve/dwarf/debug/dwarf"
)
// TODO: error reporting detail
......
......@@ -14,7 +14,7 @@ import (
"io"
"os"
"golang.org/x/debug/dwarf"
"github.com/derekparker/delve/dwarf/debug/dwarf"
)
// A File represents an open Mach-O file.
......
......@@ -3,8 +3,8 @@ package reader
import (
"errors"
"fmt"
"golang.org/x/debug/dwarf"
"github.com/derekparker/delve/dwarf/debug/dwarf"
"github.com/derekparker/delve/dwarf/op"
)
......
......@@ -24,10 +24,10 @@ type Breakpoint struct {
Temp bool // Whether this is a temp breakpoint (for next'ing).
// Breakpoint information
Tracepoint bool // Tracepoint flag
Goroutine bool // Retrieve goroutine information
Stacktrace int // Number of stack frames to retrieve
Variables []string // Variables to evaluate
Tracepoint bool // Tracepoint flag
Goroutine bool // Retrieve goroutine information
Stacktrace int // Number of stack frames to retrieve
Variables []string // Variables to evaluate
LoadArgs *LoadConfig
LoadLocals *LoadConfig
HitCount map[int]uint64 // Number of times a breakpoint has been reached in a certain goroutine
......
......@@ -11,8 +11,8 @@ import (
"go/token"
"reflect"
"github.com/derekparker/delve/dwarf/debug/dwarf"
"github.com/derekparker/delve/dwarf/reader"
"golang.org/x/debug/dwarf"
)
// EvalExpression returns the value of the given expression.
......
package proc
import (
"go/constant"
)
// delve counterpart to runtime.moduledata
type moduleData struct {
types, etypes uintptr
}
func (dbp *Process) loadModuleData() (err error) {
dbp.loadModuleDataOnce.Do(func() {
scope := &EvalScope{Thread: dbp.CurrentThread, PC: 0, CFA: 0}
var md *Variable
md, err = scope.packageVarAddr("runtime.firstmoduledata")
if err != nil {
return
}
for md.Addr != 0 {
var typesVar, etypesVar, nextVar *Variable
var types, etypes uint64
if typesVar, err = md.structMember("types"); err != nil {
return
}
if etypesVar, err = md.structMember("etypes"); err != nil {
return
}
if nextVar, err = md.structMember("next"); err != nil {
return
}
if types, err = typesVar.asUint(); err != nil {
return
}
if etypes, err = etypesVar.asUint(); err != nil {
return
}
dbp.moduleData = append(dbp.moduleData, moduleData{uintptr(types), uintptr(etypes)})
md = nextVar.maybeDereference()
if md.Unreadable != nil {
err = md.Unreadable
return
}
}
})
return
}
func (dbp *Process) resolveNameOff(typeAddr uintptr, off uintptr) (uintptr, error) {
// See runtime.resolveNameOff in $GOROOT/src/runtime/type.go
if err := dbp.loadModuleData(); err != nil {
return 0, err
}
for _, md := range dbp.moduleData {
if typeAddr >= md.types && typeAddr < md.etypes {
return md.types + off, nil
}
}
scope := &EvalScope{Thread: dbp.CurrentThread, PC: 0, CFA: 0}
reflectOffs, err := scope.packageVarAddr("runtime.reflectOffs")
if err != nil {
return 0, err
}
reflectOffsm, err := reflectOffs.structMember("m")
if err != nil {
return 0, err
}
v, err := reflectOffsm.mapAccess(newConstant(constant.MakeUint64(uint64(off)), dbp.CurrentThread))
if err != nil {
return 0, err
}
resv := v.maybeDereference()
if resv.Unreadable != nil {
return 0, resv.Unreadable
}
return resv.Addr, nil
}
......@@ -14,8 +14,7 @@ import (
"strings"
"sync"
"golang.org/x/debug/dwarf"
"github.com/derekparker/delve/dwarf/debug/dwarf"
"github.com/derekparker/delve/dwarf/frame"
"github.com/derekparker/delve/dwarf/line"
"github.com/derekparker/delve/dwarf/reader"
......@@ -59,6 +58,9 @@ type Process struct {
ptraceChan chan func()
ptraceDoneChan chan interface{}
types map[string]dwarf.Offset
loadModuleDataOnce sync.Once
moduleData []moduleData
}
// New returns an initialized Process struct. Before returning,
......
......@@ -15,7 +15,7 @@ import (
"sync"
"unsafe"
"golang.org/x/debug/macho"
"github.com/derekparker/delve/dwarf/debug/macho"
"github.com/derekparker/delve/dwarf/frame"
"github.com/derekparker/delve/dwarf/line"
......
......@@ -14,10 +14,9 @@ import (
"syscall"
"time"
"golang.org/x/debug/elf"
sys "golang.org/x/sys/unix"
"github.com/derekparker/delve/dwarf/debug/elf"
"github.com/derekparker/delve/dwarf/frame"
"github.com/derekparker/delve/dwarf/line"
)
......
......@@ -13,10 +13,9 @@ import (
"syscall"
"unsafe"
"golang.org/x/debug/dwarf"
sys "golang.org/x/sys/windows"
"github.com/derekparker/delve/dwarf/debug/dwarf"
"github.com/derekparker/delve/dwarf/frame"
"github.com/derekparker/delve/dwarf/line"
)
......
......@@ -118,7 +118,7 @@ func (it *stackIterator) Next() bool {
it.frame, it.err = it.dbp.frameInfo(it.pc, it.sp, it.top)
if it.err != nil {
if _, nofde := it.err.(*frame.NoFDEForPCError); nofde && !it.top {
it.frame = Stackframe{ Current: Location{ PC: it.pc, File: "?", Line: -1 }, Call: Location{ PC: it.pc, File: "?", Line: -1 }, CFA: 0, Ret: 0 }
it.frame = Stackframe{Current: Location{PC: it.pc, File: "?", Line: -1}, Call: Location{PC: it.pc, File: "?", Line: -1}, CFA: 0, Ret: 0}
it.atend = true
it.err = nil
return true
......
......@@ -9,8 +9,7 @@ import (
"reflect"
"runtime"
"golang.org/x/debug/dwarf"
"github.com/derekparker/delve/dwarf/debug/dwarf"
"github.com/derekparker/delve/dwarf/frame"
)
......
......@@ -4,11 +4,12 @@ import (
"github.com/derekparker/delve/dwarf/reader"
"go/ast"
"go/token"
"golang.org/x/debug/dwarf"
"reflect"
"strconv"
"strings"
"sync"
"github.com/derekparker/delve/dwarf/debug/dwarf"
)
// Do not call this function directly it isn't able to deal correctly with package paths
......
......@@ -12,8 +12,7 @@ import (
"strings"
"unsafe"
"golang.org/x/debug/dwarf"
"github.com/derekparker/delve/dwarf/debug/dwarf"
"github.com/derekparker/delve/dwarf/op"
"github.com/derekparker/delve/dwarf/reader"
)
......@@ -368,7 +367,7 @@ func (gvar *Variable) parseG() (*G, error) {
if thread, ok := mem.(*Thread); ok {
id = thread.ID
}
return nil, NoGError{ tid: id }
return nil, NoGError{tid: id}
}
gvar.loadValue(loadFullValue)
if gvar.Unreadable != nil {
......@@ -627,10 +626,10 @@ func (v *Variable) structMember(memberName string) (*Variable, error) {
// not a regular struct member
for _, field := range t.Field {
isEmbeddedStructMember :=
(field.Type.String() == ("struct " + field.Name)) ||
(field.Type.String() == field.Name) ||
(len(field.Name) > 1 &&
field.Name[0] == '*' &&
field.Type.String()[1:] == ("struct "+field.Name[1:]))
field.Type.String()[1:] == field.Name[1:])
if !isEmbeddedStructMember {
continue
}
......@@ -1394,9 +1393,38 @@ func mapEvacuated(b *Variable) bool {
}
func (v *Variable) loadInterface(recurseLevel int, loadData bool, cfg LoadConfig) {
var typestring, data *Variable
var _type, str, typestring, data *Variable
var typename string
var err error
isnil := false
// An interface variable is implemented either by a runtime.iface
// struct or a runtime.eface struct. The difference being that empty
// interfaces (i.e. "interface {}") are represented by runtime.eface
// and non-empty interfaces by runtime.iface.
//
// For both runtime.ifaces and runtime.efaces the data is stored in v.data
//
// The concrete type however is stored in v.tab._type for non-empty
// interfaces and in v._type for empty interfaces.
//
// For nil empty interface variables _type will be nil, for nil
// non-empty interface variables tab will be nil
//
// In either case the _type field is a pointer to a runtime._type struct.
//
// Before go1.7 _type used to have a field named 'string' containing
// the name of the type. Since go1.7 the field has been replaced by a
// str field that contains an offset in the module data, the concrete
// type must be calculated using the str address along with the value
// of v.tab._type (v._type for empty interfaces).
//
// The following code works for both runtime.iface and runtime.eface
// and sets the go17 flag when the 'string' field can not be found
// but the str field was found
go17 := false
v.mem = cacheMemory(v.mem, v.Addr, int(v.RealType.Size()))
ityp := resolveTypedef(&v.RealType.(*dwarf.InterfaceType).TypedefType).(*dwarf.StructType)
......@@ -1405,33 +1433,42 @@ func (v *Variable) loadInterface(recurseLevel int, loadData bool, cfg LoadConfig
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 {
tab = tab.maybeDereference()
isnil = tab.Addr == 0
if !isnil {
_type, err = tab.structMember("_type")
if err != nil {
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
if err == nil {
typestring = typestring.maybeDereference()
} else {
go17 = true
str, err = _type.structMember("str")
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
_type, _ = v.toField(f)
_type = _type.maybeDereference()
isnil = _type.Addr == 0
if !isnil {
typestring, err = _type.structMember("_string")
if err == nil {
typestring = typestring.maybeDereference()
} else {
go17 = true
str, err = _type.structMember("str")
if err != nil {
v.Unreadable = fmt.Errorf("invalid interface type: %v", err)
return
}
}
} else {
typestring = typestring.maybeDereference()
}
case "data":
data, _ = v.toField(f)
......@@ -1448,17 +1485,56 @@ func (v *Variable) loadInterface(recurseLevel int, loadData bool, cfg LoadConfig
return
}
if typestring == nil || data == nil || typestring.Addr == 0 || typestring.Kind != reflect.String {
if data == nil {
v.Unreadable = fmt.Errorf("invalid interface type")
return
}
typestring.loadValue(LoadConfig{false, 0, 512, 0, 0})
if typestring.Unreadable != nil {
v.Unreadable = fmt.Errorf("invalid interface type: %v", typestring.Unreadable)
return
if go17 {
// No 'string' field use 'str' and 'runtime.firstmoduledata' to
// find out what the concrete type is
typeAddr := _type.maybeDereference().Addr
strOff, err := str.asInt()
if err != nil {
v.Unreadable = fmt.Errorf("invalid interface type: %v", err)
return
}
res, err := v.dbp.resolveNameOff(typeAddr, uintptr(strOff))
if err != nil {
v.Unreadable = fmt.Errorf("could not resolve concrete type (data: %#x): %v", data.Addr, err)
return
}
// For a description of how memory is organized for type names read
// the comment to 'type name struct' in $GOROOT/src/reflect/type.go
typdata, err := v.dbp.CurrentThread.readMemory(res, 3+v.dbp.arch.PtrSize())
if err != nil {
v.Unreadable = fmt.Errorf("could not read concrete type (data: %#v): %v", data.Addr, err)
}
nl := int(typdata[1]<<8 | typdata[2])
rawstr, err := v.dbp.CurrentThread.readMemory(res+3, nl)
typename = string(rawstr)
} else {
if typestring == nil || typestring.Addr == 0 || typestring.Kind != reflect.String {
v.Unreadable = fmt.Errorf("invalid interface type")
return
}
typestring.loadValue(LoadConfig{false, 0, 512, 0, 0})
if typestring.Unreadable != nil {
v.Unreadable = fmt.Errorf("invalid interface type: %v", typestring.Unreadable)
return
}
typename = constant.StringVal(typestring.Value)
}
t, err := parser.ParseExpr(constant.StringVal(typestring.Value))
t, err := parser.ParseExpr(typename)
if err != nil {
v.Unreadable = fmt.Errorf("invalid interface type, unparsable data type: %v", err)
return
......@@ -1466,7 +1542,7 @@ func (v *Variable) loadInterface(recurseLevel int, loadData bool, cfg LoadConfig
typ, err := v.dbp.findTypeExpr(t)
if err != nil {
v.Unreadable = fmt.Errorf("interface type \"%s\" not found for 0x%x: %v", constant.StringVal(typestring.Value), data.Addr, err)
v.Unreadable = fmt.Errorf("interface type \"%s\" not found for 0x%x: %v", typename, data.Addr, err)
return
}
......
......@@ -6,10 +6,10 @@ import (
"go/constant"
"go/printer"
"go/token"
"golang.org/x/debug/dwarf"
"reflect"
"strconv"
"github.com/derekparker/delve/dwarf/debug/dwarf"
"github.com/derekparker/delve/proc"
)
......
......@@ -581,18 +581,18 @@ func Test1ClientServer_FindLocations(t *testing.T) {
t.Fatalf("Wrong locations returned for \"/.*Type.*String/\", got: %v expected: %v and %v\n", stringAddrs, someTypeStringFuncAddr, otherTypeStringFuncAddr)
}
_, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.main", Line: 3, Tracepoint: false})
_, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.main", Line: 4, Tracepoint: false})
if err != nil {
t.Fatalf("CreateBreakpoint(): %v\n", err)
}
<-c.Continue()
locationsprog34Addr := findLocationHelper(t, c, "locationsprog.go:34", false, 1, 0)[0]
findLocationHelper(t, c, fmt.Sprintf("%s:34", testProgPath(t, "locationsprog")), false, 1, locationsprog34Addr)
findLocationHelper(t, c, "+1", false, 1, locationsprog34Addr)
findLocationHelper(t, c, "34", false, 1, locationsprog34Addr)
findLocationHelper(t, c, "-1", false, 1, findLocationHelper(t, c, "locationsprog.go:32", false, 1, 0)[0])
locationsprog35Addr := findLocationHelper(t, c, "locationsprog.go:35", false, 1, 0)[0]
findLocationHelper(t, c, fmt.Sprintf("%s:35", testProgPath(t, "locationsprog")), false, 1, locationsprog35Addr)
findLocationHelper(t, c, "+1", false, 1, locationsprog35Addr)
findLocationHelper(t, c, "35", false, 1, locationsprog35Addr)
findLocationHelper(t, c, "-1", false, 1, findLocationHelper(t, c, "locationsprog.go:33", false, 1, 0)[0])
})
withTestClient1("testnextdefer", t, func(c *rpc1.RPCClient) {
......
......@@ -588,18 +588,18 @@ func TestClientServer_FindLocations(t *testing.T) {
t.Fatalf("Wrong locations returned for \"/.*Type.*String/\", got: %v expected: %v and %v\n", stringAddrs, someTypeStringFuncAddr, otherTypeStringFuncAddr)
}
_, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.main", Line: 3, Tracepoint: false})
_, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.main", Line: 4, Tracepoint: false})
if err != nil {
t.Fatalf("CreateBreakpoint(): %v\n", err)
}
<-c.Continue()
locationsprog34Addr := findLocationHelper(t, c, "locationsprog.go:34", false, 1, 0)[0]
findLocationHelper(t, c, fmt.Sprintf("%s:34", testProgPath(t, "locationsprog")), false, 1, locationsprog34Addr)
findLocationHelper(t, c, "+1", false, 1, locationsprog34Addr)
findLocationHelper(t, c, "34", false, 1, locationsprog34Addr)
findLocationHelper(t, c, "-1", false, 1, findLocationHelper(t, c, "locationsprog.go:32", false, 1, 0)[0])
locationsprog35Addr := findLocationHelper(t, c, "locationsprog.go:35", false, 1, 0)[0]
findLocationHelper(t, c, fmt.Sprintf("%s:35", testProgPath(t, "locationsprog")), false, 1, locationsprog35Addr)
findLocationHelper(t, c, "+1", false, 1, locationsprog35Addr)
findLocationHelper(t, c, "35", false, 1, locationsprog35Addr)
findLocationHelper(t, c, "-1", false, 1, findLocationHelper(t, c, "locationsprog.go:33", false, 1, 0)[0])
})
withTestClient2("testnextdefer", t, func(c service.Client) {
......
......@@ -374,7 +374,7 @@ func TestEmbeddedStruct(t *testing.T) {
{"b.ptr.val", true, "1337", "1337", "int", nil},
{"b.C.s", true, "\"hello\"", "\"hello\"", "string", nil},
{"b.s", true, "\"hello\"", "\"hello\"", "string", nil},
{"b2", true, "main.B {main.A: struct main.A {val: 42}, *main.C: *struct main.C nil, a: main.A {val: 47}, ptr: *main.A nil}", "main.B {main.A: (*struct main.A)(0x…", "main.B", nil},
{"b2", true, "main.B {main.A: main.A {val: 42}, *main.C: *main.C nil, a: main.A {val: 47}, ptr: *main.A nil}", "main.B {main.A: (*main.A)(0x…", "main.B", nil},
}
assertNoError(p.Continue(), t, "Continue()")
......@@ -459,7 +459,7 @@ func TestEvalExpression(t *testing.T) {
{"ch1+1", false, "", "", "", fmt.Errorf("can not convert 1 constant to chan int")},
// maps
{"m1[\"Malone\"]", false, "struct main.astruct {A: 2, B: 3}", "struct main.astruct {A: 2, B: 3}", "struct main.astruct", nil},
{"m1[\"Malone\"]", false, "main.astruct {A: 2, B: 3}", "main.astruct {A: 2, B: 3}", "main.astruct", nil},
{"m2[1].B", false, "11", "11", "int", nil},
{"m2[c1.sa[2].B-4].A", false, "10", "10", "int", nil},
{"m2[*p1].B", false, "11", "11", "int", nil},
......@@ -468,10 +468,10 @@ func TestEvalExpression(t *testing.T) {
{"m1[80:]", false, "", "", "", fmt.Errorf("map index out of bounds")},
// interfaces
{"err1", true, "error(*struct main.astruct) *{A: 1, B: 2}", "error(*struct main.astruct) 0x…", "error", nil},
{"err2", true, "error(*struct main.bstruct) *{a: main.astruct {A: 1, B: 2}}", "error(*struct main.bstruct) 0x…", "error", nil},
{"err1", true, "error(*main.astruct) *{A: 1, B: 2}", "error(*main.astruct) 0x…", "error", nil},
{"err2", true, "error(*main.bstruct) *{a: main.astruct {A: 1, B: 2}}", "error(*main.bstruct) 0x…", "error", nil},
{"errnil", true, "error nil", "error nil", "error", nil},
{"iface1", true, "interface {}(*struct main.astruct) *{A: 1, B: 2}", "interface {}(*struct main.astruct) 0x…", "interface {}", nil},
{"iface1", true, "interface {}(*main.astruct) *{A: 1, B: 2}", "interface {}(*main.astruct) 0x…", "interface {}", nil},
{"iface2", true, "interface {}(*string) *\"test\"", "interface {}(*string) 0x…", "interface {}", nil},
{"iface3", true, "interface {}(*map[string]go/constant.Value) *[]", "interface {}(*map[string]go/constant.Value) 0x…", "interface {}", nil},
{"iface4", true, "interface {}(*[]go/constant.Value) *[*4]", "interface {}(*[]go/constant.Value) 0x…", "interface {}", nil},
......@@ -480,8 +480,8 @@ func TestEvalExpression(t *testing.T) {
{"err1 == iface1", false, "", "", "", fmt.Errorf("mismatched types \"error\" and \"interface {}\"")},
{"errnil == nil", false, "false", "false", "", nil},
{"nil == errnil", false, "false", "false", "", nil},
{"err1.(*main.astruct)", false, "*struct main.astruct {A: 1, B: 2}", "(*struct main.astruct)(0x…", "*struct main.astruct", nil},
{"err1.(*main.bstruct)", false, "", "", "", fmt.Errorf("interface conversion: error is *struct main.astruct, not *struct main.bstruct")},
{"err1.(*main.astruct)", false, "*main.astruct {A: 1, B: 2}", "(*main.astruct)(0x…", "*main.astruct", nil},
{"err1.(*main.bstruct)", false, "", "", "", fmt.Errorf("interface conversion: error is *main.astruct, not *main.bstruct")},
{"errnil.(*main.astruct)", false, "", "", "", fmt.Errorf("interface conversion: error is nil, not *main.astruct")},
{"const1", true, "go/constant.Value(*go/constant.int64Val) *3", "go/constant.Value(*go/constant.int64Val) 0x…", "go/constant.Value", nil},
......@@ -669,7 +669,7 @@ func TestEvalAddrAndCast(t *testing.T) {
a, err := evalVariable(p, "*"+aaddrstr, pnormalLoadConfig)
assertNoError(err, t, fmt.Sprintf("EvalExpression(*%s)", aaddrstr))
t.Logf("*%s → %s", aaddrstr, api.ConvertVar(a).SinglelineString())
assertVariable(t, a, varTest{aaddrstr, false, "struct main.astruct {A: 1, B: 2}", "", "struct main.astruct", nil})
assertVariable(t, a, varTest{aaddrstr, false, "main.astruct {A: 1, B: 2}", "", "main.astruct", nil})
})
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册