mirror of
https://github.com/go-delve/delve.git
synced 2025-10-30 02:07:58 +08:00
proc: change (*Variable).setValue for use in CallFunction
Changes (*Variable).setValue so that it can be used in CallFunction to
set up the argument frame for the function call, adding the ability to:
- nil nillable types
- set strings to the empty string
- copy from one structure to another (including strings and slices)
- convert any interface type to interface{}
- convert pointer shaped types (map, chan, pointers, and structs
consisting of a single pointer field) to interface{}
This covers all cases where an assignment statement can be evaluated
without allocating memory or calling functions in the target process.
This commit is contained in:
@ -246,6 +246,8 @@ func main() {
|
|||||||
|
|
||||||
var nilstruct *astruct = nil
|
var nilstruct *astruct = nil
|
||||||
|
|
||||||
|
s4 := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}
|
||||||
|
|
||||||
var amb1 = 1
|
var amb1 = 1
|
||||||
runtime.Breakpoint()
|
runtime.Breakpoint()
|
||||||
for amb1 := 0; amb1 < 10; amb1++ {
|
for amb1 := 0; amb1 < 10; amb1++ {
|
||||||
@ -253,5 +255,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)
|
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)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -849,6 +849,9 @@ func readType(d *dwarf.Data, name string, r *dwarf.Reader, off dwarf.Offset, typ
|
|||||||
b = t.Type.Size()
|
b = t.Type.Size()
|
||||||
case *PtrType:
|
case *PtrType:
|
||||||
b = int64(addressSize)
|
b = int64(addressSize)
|
||||||
|
case *FuncType:
|
||||||
|
// on Go < 1.10 function types do not have a DW_AT_byte_size attribute.
|
||||||
|
b = int64(addressSize)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
typ.Common().ByteSize = b
|
typ.Common().ByteSize = b
|
||||||
|
|||||||
@ -1160,10 +1160,18 @@ func (v *Variable) asUint() (uint64, error) {
|
|||||||
return n, nil
|
return n, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type typeConvErr struct {
|
||||||
|
srcType, dstType godwarf.Type
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err *typeConvErr) Error() string {
|
||||||
|
return fmt.Sprintf("can not convert value of type %s to %s", err.srcType.String(), err.dstType.String())
|
||||||
|
}
|
||||||
|
|
||||||
func (v *Variable) isType(typ godwarf.Type, kind reflect.Kind) error {
|
func (v *Variable) isType(typ godwarf.Type, kind reflect.Kind) error {
|
||||||
if v.DwarfType != nil {
|
if v.DwarfType != nil {
|
||||||
if typ != nil && typ.String() != v.RealType.String() {
|
if typ != nil && typ.String() != v.RealType.String() {
|
||||||
return fmt.Errorf("can not convert value of type %s to %s", v.DwarfType.String(), typ.String())
|
return &typeConvErr{v.DwarfType, typ}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1221,3 +1221,36 @@ func constructTypeForKind(kind int64, bi *BinaryInfo) (*godwarf.StructType, erro
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func dwarfToRuntimeType(bi *BinaryInfo, mem MemoryReadWriter, typ godwarf.Type) (typeAddr uint64, typeKind uint64, found bool, err error) {
|
||||||
|
rdr := bi.DwarfReader()
|
||||||
|
rdr.Seek(typ.Common().Offset)
|
||||||
|
e, err := rdr.Next()
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, false, err
|
||||||
|
}
|
||||||
|
off, ok := e.Val(godwarf.AttrGoRuntimeType).(uint64)
|
||||||
|
if !ok {
|
||||||
|
return 0, 0, false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := loadModuleData(bi, mem); err != nil {
|
||||||
|
return 0, 0, false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO(aarzilli): when we support plugins this should be the plugin
|
||||||
|
//corresponding to the shared object containing entry 'e'.
|
||||||
|
typeAddr = uint64(bi.moduleData[0].types) + off
|
||||||
|
|
||||||
|
rtyp, err := bi.findType("runtime._type")
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, false, err
|
||||||
|
}
|
||||||
|
_type := newVariable("", uintptr(typeAddr), rtyp, bi, mem)
|
||||||
|
kindv := _type.loadFieldNamed("kind")
|
||||||
|
if kindv.Unreadable != nil || kindv.Kind != reflect.Uint {
|
||||||
|
return 0, 0, false, fmt.Errorf("unreadable interface type: %v", kindv.Unreadable)
|
||||||
|
}
|
||||||
|
typeKind, _ = constant.Uint64Val(kindv.Value)
|
||||||
|
return typeAddr, typeKind, true, nil
|
||||||
|
}
|
||||||
|
|||||||
@ -611,17 +611,7 @@ func (scope *EvalScope) SetVariable(name, value string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
yv.loadValue(loadSingleValue)
|
return xv.setValue(yv, value)
|
||||||
|
|
||||||
if err := yv.isType(xv.RealType, xv.Kind); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if yv.Unreadable != nil {
|
|
||||||
return fmt.Errorf("Expression \"%s\" is unreadable: %v", value, yv.Unreadable)
|
|
||||||
}
|
|
||||||
|
|
||||||
return xv.setValue(yv)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// LocalVariables returns all local variables from the current function scope.
|
// LocalVariables returns all local variables from the current function scope.
|
||||||
@ -998,34 +988,110 @@ func (v *Variable) loadValueInternal(recurseLevel int, cfg LoadConfig) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *Variable) setValue(y *Variable) error {
|
// setValue writes the value of srcv to dstv.
|
||||||
var err error
|
// * If srcv is a numerical literal constant and srcv is of a compatible type
|
||||||
switch v.Kind {
|
// the necessary type conversion is performed.
|
||||||
case reflect.Float32, reflect.Float64:
|
// * If srcv is nil and dstv is of a nil'able type then dstv is nilled.
|
||||||
f, _ := constant.Float64Val(y.Value)
|
// * If srcv is the empty string and dstv is a string then dstv is set to the
|
||||||
err = v.writeFloatRaw(f, v.RealType.Size())
|
// empty string.
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
// * If dstv is an "interface {}" and srcv is either an interface (possibly
|
||||||
n, _ := constant.Int64Val(y.Value)
|
// non-empty) or a pointer shaped type (map, channel, pointer or struct
|
||||||
err = v.writeUint(uint64(n), v.RealType.Size())
|
// containing a single pointer field) the type conversion to "interface {}"
|
||||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
// is performed.
|
||||||
n, _ := constant.Uint64Val(y.Value)
|
// * If srcv and dstv have the same type and are both addressable then the
|
||||||
err = v.writeUint(n, v.RealType.Size())
|
// contents of srcv are copied byte-by-byte into dstv
|
||||||
case reflect.Bool:
|
func (dstv *Variable) setValue(srcv *Variable, srcExpr string) error {
|
||||||
err = v.writeBool(constant.BoolVal(y.Value))
|
srcv.loadValue(loadSingleValue)
|
||||||
case reflect.Complex64, reflect.Complex128:
|
|
||||||
real, _ := constant.Float64Val(constant.Real(y.Value))
|
typerr := srcv.isType(dstv.RealType, dstv.Kind)
|
||||||
imag, _ := constant.Float64Val(constant.Imag(y.Value))
|
if _, isTypeConvErr := typerr.(*typeConvErr); isTypeConvErr {
|
||||||
err = v.writeComplex(real, imag, v.RealType.Size())
|
// attempt iface -> eface and ptr-shaped -> eface conversions.
|
||||||
default:
|
return convertToEface(srcv, dstv)
|
||||||
if t, isptr := v.RealType.(*godwarf.PtrType); isptr {
|
|
||||||
err = v.writeUint(uint64(y.Children[0].Addr), int64(t.ByteSize))
|
|
||||||
} else {
|
|
||||||
return fmt.Errorf("can not set variables of type %s (not implemented)", v.Kind.String())
|
|
||||||
}
|
}
|
||||||
|
if typerr != nil {
|
||||||
|
return typerr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if srcv.Unreadable != nil {
|
||||||
|
return fmt.Errorf("Expression \"%s\" is unreadable: %v", srcExpr, srcv.Unreadable)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Numerical types
|
||||||
|
switch dstv.Kind {
|
||||||
|
case reflect.Float32, reflect.Float64:
|
||||||
|
f, _ := constant.Float64Val(srcv.Value)
|
||||||
|
return dstv.writeFloatRaw(f, dstv.RealType.Size())
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
|
n, _ := constant.Int64Val(srcv.Value)
|
||||||
|
return dstv.writeUint(uint64(n), dstv.RealType.Size())
|
||||||
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||||
|
n, _ := constant.Uint64Val(srcv.Value)
|
||||||
|
return dstv.writeUint(n, dstv.RealType.Size())
|
||||||
|
case reflect.Bool:
|
||||||
|
return dstv.writeBool(constant.BoolVal(srcv.Value))
|
||||||
|
case reflect.Complex64, reflect.Complex128:
|
||||||
|
real, _ := constant.Float64Val(constant.Real(srcv.Value))
|
||||||
|
imag, _ := constant.Float64Val(constant.Imag(srcv.Value))
|
||||||
|
return dstv.writeComplex(real, imag, dstv.RealType.Size())
|
||||||
|
}
|
||||||
|
|
||||||
|
// nilling nillable variables
|
||||||
|
if srcv == nilVariable {
|
||||||
|
return dstv.writeZero()
|
||||||
|
}
|
||||||
|
|
||||||
|
// set a string to ""
|
||||||
|
if srcv.Kind == reflect.String && srcv.Len == 0 {
|
||||||
|
return dstv.writeZero()
|
||||||
|
}
|
||||||
|
|
||||||
|
// slice assignment (this is not handled by the writeCopy below so that
|
||||||
|
// results of a reslice operation can be used here).
|
||||||
|
if srcv.Kind == reflect.Slice {
|
||||||
|
return dstv.writeSlice(srcv.Len, srcv.Cap, srcv.Base)
|
||||||
|
}
|
||||||
|
|
||||||
|
// allow any integer to be converted to any pointer
|
||||||
|
if t, isptr := dstv.RealType.(*godwarf.PtrType); isptr {
|
||||||
|
return dstv.writeUint(uint64(srcv.Children[0].Addr), int64(t.ByteSize))
|
||||||
|
}
|
||||||
|
|
||||||
|
// byte-by-byte copying for everything else, but the source must be addressable
|
||||||
|
if srcv.Addr != 0 {
|
||||||
|
return dstv.writeCopy(srcv)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("can not set variables of type %s (not implemented)", dstv.Kind.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
// convertToEface converts srcv into an "interface {}" and writes it to
|
||||||
|
// dstv.
|
||||||
|
// Dstv must be a variable of type "inteface {}" and srcv must either by an
|
||||||
|
// interface or a pointer shaped variable (map, channel, pointer or struct
|
||||||
|
// containing a single pointer)
|
||||||
|
func convertToEface(srcv, dstv *Variable) error {
|
||||||
|
if dstv.RealType.String() != "interface {}" {
|
||||||
|
return &typeConvErr{srcv.DwarfType, dstv.RealType}
|
||||||
|
}
|
||||||
|
if _, isiface := srcv.RealType.(*godwarf.InterfaceType); isiface {
|
||||||
|
// iface -> eface conversion
|
||||||
|
_type, data, _ := srcv.readInterface()
|
||||||
|
if srcv.Unreadable != nil {
|
||||||
|
return srcv.Unreadable
|
||||||
|
}
|
||||||
|
_type = _type.maybeDereference()
|
||||||
|
dstv.writeEmptyInterface(uint64(_type.Addr), data)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
typeAddr, typeKind, runtimeTypeFound, err := dwarfToRuntimeType(srcv.bi, srcv.mem, srcv.RealType)
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if !runtimeTypeFound || typeKind&kindDirectIface == 0 {
|
||||||
|
return &typeConvErr{srcv.DwarfType, dstv.RealType}
|
||||||
|
}
|
||||||
|
return dstv.writeEmptyInterface(typeAddr, srcv)
|
||||||
|
}
|
||||||
|
|
||||||
func readStringInfo(mem MemoryReadWriter, arch Arch, addr uintptr) (uintptr, int64, error) {
|
func readStringInfo(mem MemoryReadWriter, arch Arch, addr uintptr) (uintptr, int64, error) {
|
||||||
// string data structure is always two ptrs in size. Addr, followed by len
|
// string data structure is always two ptrs in size. Addr, followed by len
|
||||||
@ -1078,13 +1144,19 @@ func readStringValue(mem MemoryReadWriter, addr uintptr, strlen int64, cfg LoadC
|
|||||||
return retstr, nil
|
return retstr, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
sliceArrayFieldName = "array"
|
||||||
|
sliceLenFieldName = "len"
|
||||||
|
sliceCapFieldName = "cap"
|
||||||
|
)
|
||||||
|
|
||||||
func (v *Variable) loadSliceInfo(t *godwarf.SliceType) {
|
func (v *Variable) loadSliceInfo(t *godwarf.SliceType) {
|
||||||
v.mem = cacheMemory(v.mem, v.Addr, int(t.Size()))
|
v.mem = cacheMemory(v.mem, v.Addr, int(t.Size()))
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
for _, f := range t.Field {
|
for _, f := range t.Field {
|
||||||
switch f.Name {
|
switch f.Name {
|
||||||
case "array":
|
case sliceArrayFieldName:
|
||||||
var base uint64
|
var base uint64
|
||||||
base, err = readUintRaw(v.mem, uintptr(int64(v.Addr)+f.ByteOffset), f.Type.Size())
|
base, err = readUintRaw(v.mem, uintptr(int64(v.Addr)+f.ByteOffset), f.Type.Size())
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@ -1097,14 +1169,14 @@ func (v *Variable) loadSliceInfo(t *godwarf.SliceType) {
|
|||||||
}
|
}
|
||||||
v.fieldType = ptrType.Type
|
v.fieldType = ptrType.Type
|
||||||
}
|
}
|
||||||
case "len":
|
case sliceLenFieldName:
|
||||||
lstrAddr, _ := v.toField(f)
|
lstrAddr, _ := v.toField(f)
|
||||||
lstrAddr.loadValue(loadSingleValue)
|
lstrAddr.loadValue(loadSingleValue)
|
||||||
err = lstrAddr.Unreadable
|
err = lstrAddr.Unreadable
|
||||||
if err == nil {
|
if err == nil {
|
||||||
v.Len, _ = constant.Int64Val(lstrAddr.Value)
|
v.Len, _ = constant.Int64Val(lstrAddr.Value)
|
||||||
}
|
}
|
||||||
case "cap":
|
case sliceCapFieldName:
|
||||||
cstrAddr, _ := v.toField(f)
|
cstrAddr, _ := v.toField(f)
|
||||||
cstrAddr.loadValue(loadSingleValue)
|
cstrAddr.loadValue(loadSingleValue)
|
||||||
err = cstrAddr.Unreadable
|
err = cstrAddr.Unreadable
|
||||||
@ -1366,6 +1438,56 @@ func (v *Variable) writeBool(value bool) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v *Variable) writeZero() error {
|
||||||
|
val := make([]byte, v.RealType.Size())
|
||||||
|
_, err := v.mem.WriteMemory(v.Addr, val)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeInterface writes the empty interface of type typeAddr and data as the data field.
|
||||||
|
func (v *Variable) writeEmptyInterface(typeAddr uint64, data *Variable) error {
|
||||||
|
dstType, dstData, _ := v.readInterface()
|
||||||
|
if v.Unreadable != nil {
|
||||||
|
return v.Unreadable
|
||||||
|
}
|
||||||
|
dstType.writeUint(typeAddr, dstType.RealType.Size())
|
||||||
|
dstData.writeCopy(data)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Variable) writeSlice(len, cap int64, base uintptr) error {
|
||||||
|
for _, f := range v.RealType.(*godwarf.SliceType).Field {
|
||||||
|
switch f.Name {
|
||||||
|
case sliceArrayFieldName:
|
||||||
|
arrv, _ := v.toField(f)
|
||||||
|
if err := arrv.writeUint(uint64(base), arrv.RealType.Size()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case sliceLenFieldName:
|
||||||
|
lenv, _ := v.toField(f)
|
||||||
|
if err := lenv.writeUint(uint64(len), lenv.RealType.Size()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case sliceCapFieldName:
|
||||||
|
capv, _ := v.toField(f)
|
||||||
|
if err := capv.writeUint(uint64(cap), capv.RealType.Size()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dstv *Variable) writeCopy(srcv *Variable) error {
|
||||||
|
buf := make([]byte, srcv.RealType.Size())
|
||||||
|
_, err := srcv.mem.ReadMemory(buf, srcv.Addr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = dstv.mem.WriteMemory(dstv.Addr, buf)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
func (v *Variable) readFunctionPtr() {
|
func (v *Variable) readFunctionPtr() {
|
||||||
val := make([]byte, v.bi.Arch.PtrSize())
|
val := make([]byte, v.bi.Arch.PtrSize())
|
||||||
_, err := v.mem.ReadMemory(val, v.Addr)
|
_, err := v.mem.ReadMemory(val, v.Addr)
|
||||||
@ -1674,11 +1796,7 @@ func mapEvacuated(b *Variable) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *Variable) loadInterface(recurseLevel int, loadData bool, cfg LoadConfig) {
|
func (v *Variable) readInterface() (_type, data *Variable, isnil bool) {
|
||||||
var _type, data *Variable
|
|
||||||
var err error
|
|
||||||
isnil := false
|
|
||||||
|
|
||||||
// An interface variable is implemented either by a runtime.iface
|
// An interface variable is implemented either by a runtime.iface
|
||||||
// struct or a runtime.eface struct. The difference being that empty
|
// struct or a runtime.eface struct. The difference being that empty
|
||||||
// interfaces (i.e. "interface {}") are represented by runtime.eface
|
// interfaces (i.e. "interface {}") are represented by runtime.eface
|
||||||
@ -1694,15 +1812,7 @@ func (v *Variable) loadInterface(recurseLevel int, loadData bool, cfg LoadConfig
|
|||||||
//
|
//
|
||||||
// In either case the _type field is a pointer to a runtime._type struct.
|
// 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 following code works for both runtime.iface and runtime.eface.
|
||||||
// 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
|
|
||||||
|
|
||||||
v.mem = cacheMemory(v.mem, v.Addr, int(v.RealType.Size()))
|
v.mem = cacheMemory(v.mem, v.Addr, int(v.RealType.Size()))
|
||||||
|
|
||||||
@ -1715,6 +1825,7 @@ func (v *Variable) loadInterface(recurseLevel int, loadData bool, cfg LoadConfig
|
|||||||
tab = tab.maybeDereference()
|
tab = tab.maybeDereference()
|
||||||
isnil = tab.Addr == 0
|
isnil = tab.Addr == 0
|
||||||
if !isnil {
|
if !isnil {
|
||||||
|
var err error
|
||||||
_type, err = tab.structMember("_type")
|
_type, err = tab.structMember("_type")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
v.Unreadable = fmt.Errorf("invalid interface type: %v", err)
|
v.Unreadable = fmt.Errorf("invalid interface type: %v", err)
|
||||||
@ -1723,14 +1834,16 @@ func (v *Variable) loadInterface(recurseLevel int, loadData bool, cfg LoadConfig
|
|||||||
}
|
}
|
||||||
case "_type": // for runtime.eface
|
case "_type": // for runtime.eface
|
||||||
_type, _ = v.toField(f)
|
_type, _ = v.toField(f)
|
||||||
_type = _type.maybeDereference()
|
isnil = _type.maybeDereference().Addr == 0
|
||||||
isnil = _type.Addr == 0
|
|
||||||
if !isnil {
|
|
||||||
}
|
|
||||||
case "data":
|
case "data":
|
||||||
data, _ = v.toField(f)
|
data, _ = v.toField(f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Variable) loadInterface(recurseLevel int, loadData bool, cfg LoadConfig) {
|
||||||
|
_type, data, isnil := v.readInterface()
|
||||||
|
|
||||||
if isnil {
|
if isnil {
|
||||||
// interface to nil
|
// interface to nil
|
||||||
|
|||||||
@ -216,6 +216,54 @@ func TestVariableEvaluation(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSetVariable(t *testing.T) {
|
||||||
|
var testcases = []struct {
|
||||||
|
name string
|
||||||
|
typ string // type of <name>
|
||||||
|
startVal string // original value of <name>
|
||||||
|
expr string
|
||||||
|
finalVal string // new value of <name> after executing <name> = <expr>
|
||||||
|
}{
|
||||||
|
{"b.ptr", "*main.A", "*main.A {val: 1337}", "nil", "*main.A nil"},
|
||||||
|
{"m2", "map[int]*main.astruct", "map[int]*main.astruct [1: *{A: 10, B: 11}, ]", "nil", "map[int]*main.astruct nil"},
|
||||||
|
{"fn1", "main.functype", "main.afunc", "nil", "nil"},
|
||||||
|
{"ch1", "chan int", "chan int 4/10", "nil", "chan int nil"},
|
||||||
|
{"s2", "[]main.astruct", "[]main.astruct len: 8, cap: 8, [{A: 1, B: 2},{A: 3, B: 4},{A: 5, B: 6},{A: 7, B: 8},{A: 9, B: 10},{A: 11, B: 12},{A: 13, B: 14},{A: 15, B: 16}]", "nil", "[]main.astruct len: 0, cap: 0, nil"},
|
||||||
|
{"err1", "error", "error(*main.astruct) *{A: 1, B: 2}", "nil", "error nil"},
|
||||||
|
{"s1[0]", "string", `"one"`, `""`, `""`},
|
||||||
|
{"as1", "main.astruct", "main.astruct {A: 1, B: 1}", `m1["Malone"]`, "main.astruct {A: 2, B: 3}"},
|
||||||
|
|
||||||
|
{"iface1", "interface {}", "interface {}(*main.astruct) *{A: 1, B: 2}", "nil", "interface {} nil"},
|
||||||
|
{"iface1", "interface {}", "interface {} nil", "iface2", "interface {}(string) \"test\""},
|
||||||
|
{"iface1", "interface {}", "interface {}(string) \"test\"", "parr", "interface {}(*[4]int) *[0,1,2,3]"},
|
||||||
|
|
||||||
|
{"s3", "[]int", `[]int len: 0, cap: 6, []`, "s4[2:5]", "[]int len: 3, cap: 3, [3,4,5]"},
|
||||||
|
{"s3", "[]int", "[]int len: 3, cap: 3, [3,4,5]", "arr1[:]", "[]int len: 4, cap: 4, [0,1,2,3]"},
|
||||||
|
}
|
||||||
|
|
||||||
|
withTestProcess("testvariables2", t, func(p proc.Process, fixture protest.Fixture) {
|
||||||
|
assertNoError(proc.Continue(p), t, "Continue()")
|
||||||
|
|
||||||
|
for _, tc := range testcases {
|
||||||
|
if tc.name == "iface1" && tc.expr == "parr" {
|
||||||
|
if !goversion.VersionAfterOrEqual(runtime.Version(), 1, 11) {
|
||||||
|
// conversion pointer -> eface not supported prior to Go 1.11
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
variable, err := evalVariable(p, tc.name, pnormalLoadConfig)
|
||||||
|
assertNoError(err, t, "EvalVariable()")
|
||||||
|
assertVariable(t, variable, varTest{tc.name, true, tc.startVal, "", tc.typ, nil})
|
||||||
|
|
||||||
|
assertNoError(setVariable(p, tc.name, tc.expr), t, "SetVariable()")
|
||||||
|
|
||||||
|
variable, err = evalVariable(p, tc.name, pnormalLoadConfig)
|
||||||
|
assertNoError(err, t, "EvalVariable()")
|
||||||
|
assertVariable(t, variable, varTest{tc.name, true, tc.finalVal, "", tc.typ, nil})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestVariableEvaluationShort(t *testing.T) {
|
func TestVariableEvaluationShort(t *testing.T) {
|
||||||
testcases := []varTest{
|
testcases := []varTest{
|
||||||
{"a1", true, "\"foofoofoofoofoofoo\"", "", "string", nil},
|
{"a1", true, "\"foofoofoofoofoofoo\"", "", "string", nil},
|
||||||
|
|||||||
Reference in New Issue
Block a user