proc/eval: support for simple builtin functions

supported: len, cap, imag, real, complex
This commit is contained in:
aarzilli 2015-11-12 18:53:01 +01:00 committed by Derek Parker
parent 48e13a9045
commit 141fc4ed21
4 changed files with 237 additions and 58 deletions

@ -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)
}

@ -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'.

@ -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(<float const>, <float const>) and complex128(<float const>, <float const>)
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 {

@ -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\"")},