diff --git a/_fixtures/testvariables3.go b/_fixtures/testvariables3.go index aa69d7a8..fe03556b 100644 --- a/_fixtures/testvariables3.go +++ b/_fixtures/testvariables3.go @@ -41,6 +41,7 @@ func main() { i3 := 3 p1 := &i1 s1 := []string{"one", "two", "three", "four", "five"} + s3 := make([]int, 0, 6) a1 := [5]string{"one", "two", "three", "four", "five"} c1 := cstruct{&bstruct{astruct{1, 2}}, []*astruct{&astruct{1, 2}, &astruct{2, 3}, &astruct{4, 5}}} s2 := []astruct{{1, 2}, {3, 4}, {5, 6}, {7, 8}, {9, 10}, {11, 12}, {13, 14}, {15, 16}} @@ -109,11 +110,14 @@ func main() { var errnil error = nil var iface1 interface{} = c1.sa[0] var ifacenil interface{} = nil + arr1 := [4]int{0, 1, 2, 3} + parr := &arr1 + cpx1 := complex(1, 2) var amb1 = 1 runtime.Breakpoint() for amb1 := 0; amb1 < 10; amb1++ { fmt.Println(amb1) } - fmt.Println(i1, i2, i3, p1, amb1, s1, 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, ifacenil) + 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, ifacenil, arr1, parr, cpx1) } diff --git a/dwarf/reader/reader.go b/dwarf/reader/reader.go index f25f4016..fc23c45d 100755 --- a/dwarf/reader/reader.go +++ b/dwarf/reader/reader.go @@ -104,6 +104,8 @@ func (reader *Reader) AddrForMember(member string, initialInstructions []byte) ( } } +var TypeNotFoundErr = errors.New("no type entry found") + // SeekToType moves the reader to the type specified by the entry, // optionally resolving typedefs and pointer types. If the reader is set // to a struct type the NextMemberVariable call can be used to walk all member data. @@ -138,7 +140,7 @@ func (reader *Reader) SeekToType(entry *dwarf.Entry, resolveTypedefs bool, resol reader.Seek(offset) } - return nil, fmt.Errorf("no type entry found") + return nil, TypeNotFoundErr } // SeekToTypeNamed moves the reader to the type specified by the name. @@ -168,7 +170,7 @@ func (reader *Reader) SeekToTypeNamed(name string) (*dwarf.Entry, error) { } } - return nil, errors.New("no type entry found") + return nil, TypeNotFoundErr } // Finds the entry for 'name'. diff --git a/proc/eval.go b/proc/eval.go index ca789797..7f474dc5 100644 --- a/proc/eval.go +++ b/proc/eval.go @@ -5,6 +5,7 @@ import ( "debug/dwarf" "encoding/binary" "fmt" + "github.com/derekparker/delve/dwarf/reader" "go/ast" "go/constant" "go/parser" @@ -31,12 +32,16 @@ func (scope *EvalScope) EvalExpression(expr string) (*Variable, error) { func (scope *EvalScope) evalAST(t ast.Expr) (*Variable, error) { switch node := t.(type) { case *ast.CallExpr: - if fnnode, ok := node.Fun.(*ast.Ident); ok && len(node.Args) == 2 && (fnnode.Name == "complex64" || fnnode.Name == "complex128") { - // implement the special case type casts complex64(f1, f2) and complex128(f1, f2) - return scope.evalComplexCast(fnnode.Name, node) + if len(node.Args) == 1 { + v, err := scope.evalTypeCast(node) + if err == nil { + return v, nil + } + if err != reader.TypeNotFoundErr { + return v, err + } } - // this must be a type cast because we do not support function calls - return scope.evalTypeCast(node) + return scope.evalBuiltinCall(node) case *ast.Ident: return scope.evalIdent(node) @@ -99,56 +104,8 @@ func exprToString(t ast.Expr) string { return buf.String() } -// Eval expressions: complex64(, ) and complex128(, ) -func (scope *EvalScope) evalComplexCast(typename string, node *ast.CallExpr) (*Variable, error) { - realev, err := scope.evalAST(node.Args[0]) - if err != nil { - return nil, err - } - imagev, err := scope.evalAST(node.Args[1]) - if err != nil { - return nil, err - } - - sz := 128 - ftypename := "float64" - if typename == "complex64" { - sz = 64 - ftypename = "float32" - } - - realev.loadValue() - imagev.loadValue() - - if realev.Unreadable != nil { - return nil, realev.Unreadable - } - - if imagev.Unreadable != nil { - return nil, imagev.Unreadable - } - - if realev.Value == nil || ((realev.Value.Kind() != constant.Int) && (realev.Value.Kind() != constant.Float)) { - return nil, fmt.Errorf("can not convert \"%s\" to %s", exprToString(node.Args[0]), ftypename) - } - - if imagev.Value == nil || ((imagev.Value.Kind() != constant.Int) && (imagev.Value.Kind() != constant.Float)) { - return nil, fmt.Errorf("can not convert \"%s\" to %s", exprToString(node.Args[1]), ftypename) - } - - typ := &dwarf.ComplexType{dwarf.BasicType{dwarf.CommonType{ByteSize: int64(sz / 8), Name: typename}, int64(sz), 0}} - - r := newVariable("", 0, typ, scope.Thread) - r.Value = constant.BinaryOp(realev.Value, token.ADD, constant.MakeImag(imagev.Value)) - return r, nil -} - // Eval type cast expressions func (scope *EvalScope) evalTypeCast(node *ast.CallExpr) (*Variable, error) { - if len(node.Args) != 1 { - return nil, fmt.Errorf("wrong number of arguments for a type cast") - } - argv, err := scope.evalAST(node.Args[0]) if err != nil { return nil, err @@ -265,6 +222,200 @@ func convertInt(n uint64, signed bool, size int64) uint64 { return uint64(binary.BigEndian.Uint64(buf)) } +func (scope *EvalScope) evalBuiltinCall(node *ast.CallExpr) (*Variable, error) { + fnnode, ok := node.Fun.(*ast.Ident) + if !ok { + return nil, fmt.Errorf("function calls are not supported") + } + + args := make([]*Variable, len(node.Args)) + + for i := range node.Args { + v, err := scope.evalAST(node.Args[i]) + if err != nil { + return nil, err + } + args[i] = v + } + + switch fnnode.Name { + case "cap": + return capBuiltin(args, node.Args) + case "len": + return lenBuiltin(args, node.Args) + case "complex": + return complexBuiltin(args, node.Args) + case "imag": + return imagBuiltin(args, node.Args) + case "real": + return realBuiltin(args, node.Args) + } + + return nil, fmt.Errorf("function calls are not supported") +} + +func capBuiltin(args []*Variable, nodeargs []ast.Expr) (*Variable, error) { + if len(args) != 1 { + return nil, fmt.Errorf("wrong number of arguments to cap: %d", len(args)) + } + + arg := args[0] + invalidArgErr := fmt.Errorf("invalid argument %s (type %s) for cap", exprToString(nodeargs[0]), arg.TypeString()) + + switch arg.Kind { + case reflect.Ptr: + arg = arg.maybeDereference() + if arg.Kind != reflect.Array { + return nil, invalidArgErr + } + fallthrough + case reflect.Array: + return newConstant(constant.MakeInt64(arg.Len), arg.thread), nil + case reflect.Slice: + return newConstant(constant.MakeInt64(arg.Cap), arg.thread), nil + case reflect.Chan: + arg.loadValue() + if arg.Unreadable != nil { + return nil, arg.Unreadable + } + if arg.base == 0 { + return newConstant(constant.MakeInt64(0), arg.thread), nil + } + return newConstant(arg.Children[1].Value, arg.thread), nil + default: + return nil, invalidArgErr + } +} + +func lenBuiltin(args []*Variable, nodeargs []ast.Expr) (*Variable, error) { + if len(args) != 1 { + return nil, fmt.Errorf("wrong number of arguments to len: %d", len(args)) + } + arg := args[0] + invalidArgErr := fmt.Errorf("invalid argument %s (type %s) for len", exprToString(nodeargs[0]), arg.TypeString()) + + switch arg.Kind { + case reflect.Ptr: + arg = arg.maybeDereference() + if arg.Kind != reflect.Array { + return nil, invalidArgErr + } + fallthrough + case reflect.Array, reflect.Slice, reflect.String: + if arg.Unreadable != nil { + return nil, arg.Unreadable + } + return newConstant(constant.MakeInt64(arg.Len), arg.thread), nil + case reflect.Chan: + arg.loadValue() + if arg.Unreadable != nil { + return nil, arg.Unreadable + } + if arg.base == 0 { + return newConstant(constant.MakeInt64(0), arg.thread), nil + } + return newConstant(arg.Children[0].Value, arg.thread), nil + case reflect.Map: + it := arg.mapIterator() + if arg.Unreadable != nil { + return nil, arg.Unreadable + } + if it == nil { + return newConstant(constant.MakeInt64(0), arg.thread), nil + } + return newConstant(constant.MakeInt64(arg.Len), arg.thread), nil + default: + return nil, invalidArgErr + } +} + +func complexBuiltin(args []*Variable, nodeargs []ast.Expr) (*Variable, error) { + if len(args) != 2 { + return nil, fmt.Errorf("wrong number of arguments to complex: %d", len(args)) + } + + realev := args[0] + imagev := args[1] + + realev.loadValue() + imagev.loadValue() + + if realev.Unreadable != nil { + return nil, realev.Unreadable + } + + if imagev.Unreadable != nil { + return nil, imagev.Unreadable + } + + if realev.Value == nil || ((realev.Value.Kind() != constant.Int) && (realev.Value.Kind() != constant.Float)) { + return nil, fmt.Errorf("invalid argument 1 %s (type %s) to complex", exprToString(nodeargs[0]), realev.TypeString()) + } + + if imagev.Value == nil || ((imagev.Value.Kind() != constant.Int) && (imagev.Value.Kind() != constant.Float)) { + return nil, fmt.Errorf("invalid argument 2 %s (type %s) to complex", exprToString(nodeargs[1]), imagev.TypeString()) + } + + sz := int64(0) + if realev.RealType != nil { + sz = realev.RealType.(*dwarf.FloatType).Size() + } + if imagev.RealType != nil { + isz := imagev.RealType.(*dwarf.FloatType).Size() + if isz > sz { + sz = isz + } + } + + if sz == 0 { + sz = 128 + } + + typ := &dwarf.ComplexType{dwarf.BasicType{dwarf.CommonType{ByteSize: int64(sz / 8), Name: fmt.Sprintf("complex%d", sz)}, sz, 0}} + + r := newVariable("", 0, typ, realev.thread) + r.Value = constant.BinaryOp(realev.Value, token.ADD, constant.MakeImag(imagev.Value)) + return r, nil +} + +func imagBuiltin(args []*Variable, nodeargs []ast.Expr) (*Variable, error) { + if len(args) != 1 { + return nil, fmt.Errorf("wrong number of arguments to imag: %d", len(args)) + } + + arg := args[0] + arg.loadValue() + + if arg.Unreadable != nil { + return nil, arg.Unreadable + } + + if arg.Kind != reflect.Complex64 && arg.Kind != reflect.Complex128 { + return nil, fmt.Errorf("invalid argument %s (type %s) to imag", exprToString(nodeargs[0]), arg.TypeString()) + } + + return newConstant(constant.Imag(arg.Value), arg.thread), nil +} + +func realBuiltin(args []*Variable, nodeargs []ast.Expr) (*Variable, error) { + if len(args) != 1 { + return nil, fmt.Errorf("wrong number of arguments to real: %d", len(args)) + } + + arg := args[0] + arg.loadValue() + + if arg.Unreadable != nil { + return nil, arg.Unreadable + } + + if arg.Value == nil || ((arg.Value.Kind() != constant.Int) && (arg.Value.Kind() != constant.Float) && (arg.Value.Kind() != constant.Complex)) { + return nil, fmt.Errorf("invalid argument %s (type %s) to real", exprToString(nodeargs[0]), arg.TypeString()) + } + + return newConstant(constant.Real(arg.Value), arg.thread), nil +} + // Evaluates identifier expressions func (scope *EvalScope) evalIdent(node *ast.Ident) (*Variable, error) { switch node.Name { diff --git a/service/test/variables_test.go b/service/test/variables_test.go index 847d2fca..58b8ace6 100644 --- a/service/test/variables_test.go +++ b/service/test/variables_test.go @@ -344,7 +344,7 @@ func TestComplexSetting(t *testing.T) { h("3.2i", "(0 + 3.2i)") h("1.1", "(1.1 + 0i)") h("1 + 3.3i", "(1 + 3.3i)") - h("complex128(1.2, 3.4)", "(1.2 + 3.4i)") + h("complex(1.2, 3.4)", "(1.2 + 3.4i)") }) } @@ -462,6 +462,28 @@ func TestEvalExpression(t *testing.T) { {"c1.pb.a == *(c1.sa[1])", false, "false", "", "", nil}, {"c1.pb.a != *(c1.sa[1])", false, "true", "", "", nil}, + // builtins + {"cap(parr)", false, "4", "", "", nil}, + {"len(parr)", false, "4", "", "", nil}, + {"cap(p1)", false, "", "", "", fmt.Errorf("invalid argument p1 (type *int) for cap")}, + {"len(p1)", false, "", "", "", fmt.Errorf("invalid argument p1 (type *int) for len")}, + {"cap(a1)", false, "5", "", "", nil}, + {"len(a1)", false, "5", "", "", nil}, + {"cap(s3)", false, "6", "", "", nil}, + {"len(s3)", false, "0", "", "", nil}, + {"cap(nilslice)", false, "0", "", "", nil}, + {"len(nilslice)", false, "0", "", "", nil}, + {"cap(ch1)", false, "2", "", "", nil}, + {"len(ch1)", false, "0", "", "", nil}, + {"cap(chnil)", false, "0", "", "", nil}, + {"len(chnil)", false, "0", "", "", nil}, + {"len(m1)", false, "41", "", "", nil}, + {"len(mnil)", false, "0", "", "", nil}, + {"imag(cpx1)", false, "2", "", "", nil}, + {"real(cpx1)", false, "1", "", "", nil}, + {"imag(3i)", false, "3", "", "", nil}, + {"real(4)", false, "4", "", "", nil}, + // nil {"nil", false, "nil", "", "", nil}, {"nil+1", false, "", "", "", fmt.Errorf("operator + can not be applied to \"nil\"")}, @@ -504,7 +526,7 @@ func TestEvalExpression(t *testing.T) { {"i2 << i3", false, "", "", "int", fmt.Errorf("shift count type int, must be unsigned integer")}, {"*(i2 + i3)", false, "", "", "", fmt.Errorf("expression \"(i2 + i3)\" (int) can not be dereferenced")}, {"i2.member", false, "", "", "", fmt.Errorf("i2 (type int) is not a struct")}, - {"fmt.Println(\"hello\")", false, "", "", "", fmt.Errorf("no type entry found")}, + {"fmt.Println(\"hello\")", false, "", "", "", fmt.Errorf("function calls are not supported")}, {"*nil", false, "", "", "", fmt.Errorf("nil can not be dereferenced")}, {"!nil", false, "", "", "", fmt.Errorf("operator ! can not be applied to \"nil\"")}, {"&nil", false, "", "", "", fmt.Errorf("can not take address of \"nil\"")},