proc: Implements expression interpreter
Supported operators: - All (binary and unary) operators between basic types except <-, ++ and -- (includes & to take the address of an expression) - Comparison operators between supported compound types - Typecast of integer constants into pointer types - struct members - indexing of arrays, slices and strings - slicing of arrays, slices and strings - pointer dereferencing - true, false and nil constants Implements #116, #117 and #251
This commit is contained in:
parent
6f4401654c
commit
43b64ec39e
@ -5,10 +5,49 @@ import (
|
||||
"runtime"
|
||||
)
|
||||
|
||||
type astruct struct {
|
||||
A int
|
||||
B int
|
||||
}
|
||||
|
||||
type bstruct struct {
|
||||
a astruct
|
||||
}
|
||||
|
||||
type cstruct struct {
|
||||
pb *bstruct
|
||||
sa []*astruct
|
||||
}
|
||||
|
||||
func afunc(x int) int {
|
||||
return x + 2
|
||||
}
|
||||
|
||||
type functype func(int) int
|
||||
|
||||
func main() {
|
||||
i1 := 1
|
||||
i2 := 2
|
||||
f1 := 3.0
|
||||
i3 := 3
|
||||
p1 := &i1
|
||||
s1 := []string{"one", "two", "three", "four", "five"}
|
||||
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}}
|
||||
p2 := &(c1.sa[2].B)
|
||||
as1 := astruct{1, 1}
|
||||
var p3 *int
|
||||
str1 := "01234567890"
|
||||
var fn1 functype = afunc
|
||||
var fn2 functype = nil
|
||||
var nilslice []int = nil
|
||||
var nilptr *int = nil
|
||||
|
||||
var amb1 = 1
|
||||
runtime.Breakpoint()
|
||||
fmt.Println(i1, i2, p1)
|
||||
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)
|
||||
}
|
||||
|
@ -22,8 +22,9 @@ type B struct {
|
||||
|
||||
func main() {
|
||||
b := B{A: A{-314}, C: &C{"hello"}, a: A{42}, ptr: &A{1337}}
|
||||
b2 := B{A: A{42}, a: A{47}}
|
||||
runtime.Breakpoint()
|
||||
fmt.Println(b)
|
||||
fmt.Println(b, b2)
|
||||
fmt.Println(b.val)
|
||||
fmt.Println(b.A.val)
|
||||
fmt.Println(b.a.val)
|
||||
|
@ -151,6 +151,13 @@ func (reader *Reader) SeekToTypeNamed(name string) (*dwarf.Entry, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch entry.Tag {
|
||||
case dwarf.TagArrayType, dwarf.TagBaseType, dwarf.TagClassType, dwarf.TagStructType, dwarf.TagUnionType, dwarf.TagConstType, dwarf.TagVolatileType, dwarf.TagRestrictType, dwarf.TagEnumerationType, dwarf.TagPointerType, dwarf.TagSubroutineType, dwarf.TagTypedef, dwarf.TagUnspecifiedType:
|
||||
//ok
|
||||
default:
|
||||
continue
|
||||
}
|
||||
|
||||
n, ok := entry.Val(dwarf.AttrName).(string)
|
||||
if !ok {
|
||||
continue
|
||||
|
759
proc/eval.go
Normal file
759
proc/eval.go
Normal file
@ -0,0 +1,759 @@
|
||||
package proc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"debug/dwarf"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/constant"
|
||||
"go/parser"
|
||||
"go/printer"
|
||||
"go/token"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// Returns the value of the given expression
|
||||
func (scope *EvalScope) EvalExpression(expr string) (*Variable, error) {
|
||||
t, err := parser.ParseExpr(expr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ev, err := scope.evalAST(t)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ev.loadValue()
|
||||
return ev, nil
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
// this must be a type cast because we do not support function calls
|
||||
return scope.evalTypeCast(node)
|
||||
|
||||
case *ast.Ident:
|
||||
return scope.evalIdent(node)
|
||||
|
||||
case *ast.ParenExpr:
|
||||
// otherwise just eval recursively
|
||||
return scope.evalAST(node.X)
|
||||
|
||||
case *ast.SelectorExpr: // <expression>.<identifier>
|
||||
// try to interpret the selector as a package variable
|
||||
if maybePkg, ok := node.X.(*ast.Ident); ok {
|
||||
if v, err := scope.packageVarAddr(maybePkg.Name + "." + node.Sel.Name); err == nil {
|
||||
return v, nil
|
||||
}
|
||||
}
|
||||
// if it's not a package variable then it must be a struct member access
|
||||
return scope.evalStructSelector(node)
|
||||
|
||||
case *ast.IndexExpr:
|
||||
return scope.evalIndex(node)
|
||||
|
||||
case *ast.SliceExpr:
|
||||
if node.Slice3 {
|
||||
return nil, fmt.Errorf("3-index slice expressions not supported")
|
||||
}
|
||||
return scope.evalReslice(node)
|
||||
|
||||
case *ast.StarExpr:
|
||||
// pointer dereferencing *<expression>
|
||||
return scope.evalPointerDeref(node)
|
||||
|
||||
case *ast.UnaryExpr:
|
||||
// The unary operators we support are +, - and & (note that unary * is parsed as ast.StarExpr)
|
||||
switch node.Op {
|
||||
case token.AND:
|
||||
return scope.evalAddrOf(node)
|
||||
|
||||
default:
|
||||
return scope.evalUnary(node)
|
||||
}
|
||||
|
||||
case *ast.BinaryExpr:
|
||||
return scope.evalBinary(node)
|
||||
|
||||
case *ast.BasicLit:
|
||||
return newConstant(constant.MakeFromLiteral(node.Value, node.Kind, 0), scope.Thread), nil
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("expression %T not implemented", t)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func exprToString(t ast.Expr) string {
|
||||
var buf bytes.Buffer
|
||||
printer.Fprint(&buf, token.NewFileSet(), t)
|
||||
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
|
||||
}
|
||||
argv.loadValue()
|
||||
if argv.Unreadable != nil {
|
||||
return nil, argv.Unreadable
|
||||
}
|
||||
|
||||
fnnode := node.Fun
|
||||
|
||||
// remove all enclosing parenthesis from the type name
|
||||
for {
|
||||
p, ok := fnnode.(*ast.ParenExpr)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
fnnode = p.X
|
||||
}
|
||||
|
||||
var typ dwarf.Type
|
||||
|
||||
if snode, ok := fnnode.(*ast.StarExpr); ok {
|
||||
// Pointer types only appear in the dwarf informations when
|
||||
// a pointer to the type is used in the target program, here
|
||||
// we create a pointer type on the fly so that the user can
|
||||
// specify a pointer to any variable used in the target program
|
||||
ptyp, err := scope.findType(exprToString(snode.X))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
typ = &dwarf.PtrType{dwarf.CommonType{int64(scope.Thread.dbp.arch.PtrSize()), exprToString(fnnode)}, ptyp}
|
||||
} else {
|
||||
typ, err = scope.findType(exprToString(fnnode))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// only supports cast of integer constants into pointers
|
||||
ptyp, isptrtyp := typ.(*dwarf.PtrType)
|
||||
if !isptrtyp {
|
||||
return nil, fmt.Errorf("can not convert \"%s\" to %s", exprToString(node.Args[0]), typ.String())
|
||||
}
|
||||
|
||||
switch argv.Kind {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
// ok
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
// ok
|
||||
default:
|
||||
return nil, fmt.Errorf("can not convert \"%s\" to %s", exprToString(node.Args[0]), typ.String())
|
||||
}
|
||||
|
||||
n, _ := constant.Int64Val(argv.Value)
|
||||
|
||||
v := newVariable("", 0, ptyp, scope.Thread)
|
||||
v.Children = []Variable{*newVariable("", uintptr(n), ptyp.Type, scope.Thread)}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// Evaluates identifier expressions
|
||||
func (scope *EvalScope) evalIdent(node *ast.Ident) (*Variable, error) {
|
||||
switch node.Name {
|
||||
case "true", "false":
|
||||
return newConstant(constant.MakeBool(node.Name == "true"), scope.Thread), nil
|
||||
case "nil":
|
||||
return nilVariable, nil
|
||||
}
|
||||
|
||||
// try to interpret this as a local variable
|
||||
v, err := scope.extractVarInfo(node.Name)
|
||||
if err != nil {
|
||||
// if it's not a local variable then it could be a package variable w/o explicit package name
|
||||
origErr := err
|
||||
_, _, fn := scope.Thread.dbp.PCToLine(scope.PC)
|
||||
if fn != nil {
|
||||
if v, err := scope.packageVarAddr(fn.PackageName() + "." + node.Name); err == nil {
|
||||
v.Name = node.Name
|
||||
return v, nil
|
||||
}
|
||||
}
|
||||
return nil, origErr
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// Evaluates expressions <subexpr>.<field name> where subexpr is not a package name
|
||||
func (scope *EvalScope) evalStructSelector(node *ast.SelectorExpr) (*Variable, error) {
|
||||
xv, err := scope.evalAST(node.X)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return xv.structMember(node.Sel.Name)
|
||||
}
|
||||
|
||||
// Evaluates expressions <subexpr>[<subexpr>] (subscript access to arrays, slices and maps)
|
||||
func (scope *EvalScope) evalIndex(node *ast.IndexExpr) (*Variable, error) {
|
||||
xev, err := scope.evalAST(node.X)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if xev.Unreadable != nil {
|
||||
return nil, xev.Unreadable
|
||||
}
|
||||
if xev.base == 0 {
|
||||
return nil, fmt.Errorf("can not index \"%s\"", exprToString(node.X))
|
||||
}
|
||||
|
||||
idxev, err := scope.evalAST(node.Index)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch xev.Kind {
|
||||
case reflect.Slice, reflect.Array, reflect.String:
|
||||
n, err := idxev.asInt()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return xev.sliceAccess(int(n))
|
||||
|
||||
case reflect.Map:
|
||||
return nil, fmt.Errorf("map access not implemented")
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid expression \"%s\" (type %s does not support indexing)", exprToString(node.X), xev.DwarfType.String())
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Evaluates expressions <subexpr>[<subexpr>:<subexpr>]
|
||||
// HACK: slicing a map expression with [0:0] will return the whole map
|
||||
func (scope *EvalScope) evalReslice(node *ast.SliceExpr) (*Variable, error) {
|
||||
xev, err := scope.evalAST(node.X)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if xev.Unreadable != nil {
|
||||
return nil, xev.Unreadable
|
||||
}
|
||||
if xev.base == 0 {
|
||||
return nil, fmt.Errorf("can not slice \"%s\"", exprToString(node.X))
|
||||
}
|
||||
|
||||
switch xev.Kind {
|
||||
case reflect.Slice, reflect.Array, reflect.String:
|
||||
//ok
|
||||
default:
|
||||
return nil, fmt.Errorf("cannot slice \"%s\" (type %s)", exprToString(node.X), xev.DwarfType.String())
|
||||
}
|
||||
|
||||
var low, high int64
|
||||
|
||||
if node.Low != nil {
|
||||
lowv, err := scope.evalAST(node.Low)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
low, err = lowv.asInt()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can not convert \"%s\" to int: %v", exprToString(node.Low), err)
|
||||
}
|
||||
}
|
||||
|
||||
if node.High == nil {
|
||||
high = xev.Len
|
||||
} else {
|
||||
highv, err := scope.evalAST(node.High)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
high, err = highv.asInt()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can not convert \"%s\" to int: %v", exprToString(node.High), err)
|
||||
}
|
||||
}
|
||||
|
||||
r, err := xev.reslice(low, high)
|
||||
return r, err
|
||||
}
|
||||
|
||||
// Evaluates a pointer dereference expression: *<subexpr>
|
||||
func (scope *EvalScope) evalPointerDeref(node *ast.StarExpr) (*Variable, error) {
|
||||
xev, err := scope.evalAST(node.X)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if xev.DwarfType == nil {
|
||||
return nil, fmt.Errorf("expression \"%s\" can not be dereferenced", exprToString(node.X))
|
||||
}
|
||||
|
||||
if xev.Kind != reflect.Ptr {
|
||||
return nil, fmt.Errorf("expression \"%s\" (%s) can not be dereferenced", exprToString(node.X), xev.DwarfType.String())
|
||||
}
|
||||
|
||||
if len(xev.Children) == 1 {
|
||||
// this branch is here to support pointers constructed with typecasts from ints
|
||||
return &(xev.Children[0]), nil
|
||||
} else {
|
||||
rv := xev.maybeDereference()
|
||||
if rv.Addr == 0 {
|
||||
return nil, fmt.Errorf("nil pointer dereference")
|
||||
}
|
||||
return rv, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Evaluates expressions &<subexpr>
|
||||
func (scope *EvalScope) evalAddrOf(node *ast.UnaryExpr) (*Variable, error) {
|
||||
xev, err := scope.evalAST(node.X)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if xev.Addr == 0 {
|
||||
return nil, fmt.Errorf("can not take address of \"%s\"", exprToString(node.X))
|
||||
}
|
||||
|
||||
xev.OnlyAddr = true
|
||||
|
||||
typename := "*" + xev.DwarfType.String()
|
||||
rv := newVariable("", 0, &dwarf.PtrType{dwarf.CommonType{ByteSize: int64(scope.Thread.dbp.arch.PtrSize()), Name: typename}, xev.DwarfType}, scope.Thread)
|
||||
rv.Children = []Variable{*xev}
|
||||
rv.loaded = true
|
||||
|
||||
return rv, nil
|
||||
}
|
||||
|
||||
func constantUnaryOp(op token.Token, y constant.Value) (r constant.Value, err error) {
|
||||
defer func() {
|
||||
if ierr := recover(); ierr != nil {
|
||||
err = fmt.Errorf("%v", ierr)
|
||||
}
|
||||
}()
|
||||
r = constant.UnaryOp(op, y, 0)
|
||||
return
|
||||
}
|
||||
|
||||
func constantBinaryOp(op token.Token, x, y constant.Value) (r constant.Value, err error) {
|
||||
defer func() {
|
||||
if ierr := recover(); ierr != nil {
|
||||
err = fmt.Errorf("%v", ierr)
|
||||
}
|
||||
}()
|
||||
switch op {
|
||||
case token.SHL, token.SHR:
|
||||
n, _ := constant.Uint64Val(y)
|
||||
r = constant.Shift(x, op, uint(n))
|
||||
default:
|
||||
r = constant.BinaryOp(x, op, y)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func constantCompare(op token.Token, x, y constant.Value) (r bool, err error) {
|
||||
defer func() {
|
||||
if ierr := recover(); ierr != nil {
|
||||
err = fmt.Errorf("%v", ierr)
|
||||
}
|
||||
}()
|
||||
r = constant.Compare(x, op, y)
|
||||
return
|
||||
}
|
||||
|
||||
// Evaluates expressions: -<subexpr> and +<subexpr>
|
||||
func (scope *EvalScope) evalUnary(node *ast.UnaryExpr) (*Variable, error) {
|
||||
xv, err := scope.evalAST(node.X)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
xv.loadValue()
|
||||
if xv.Unreadable != nil {
|
||||
return nil, xv.Unreadable
|
||||
}
|
||||
if xv.Value == nil {
|
||||
return nil, fmt.Errorf("operator %s can not be applied to \"%s\"", node.Op.String(), exprToString(node.X))
|
||||
}
|
||||
rc, err := constantUnaryOp(node.Op, xv.Value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if xv.DwarfType != nil {
|
||||
r := newVariable("", 0, xv.DwarfType, xv.thread)
|
||||
r.Value = rc
|
||||
return r, nil
|
||||
} else {
|
||||
return newConstant(rc, xv.thread), nil
|
||||
}
|
||||
}
|
||||
|
||||
func negotiateType(op token.Token, xv, yv *Variable) (dwarf.Type, error) {
|
||||
if op == token.SHR || op == token.SHL {
|
||||
if xv.Value == nil || xv.Value.Kind() != constant.Int {
|
||||
return nil, fmt.Errorf("shift of type %s", xv.Kind)
|
||||
}
|
||||
|
||||
switch yv.Kind {
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
// ok
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
if yv.DwarfType != nil || constant.Sign(yv.Value) < 0 {
|
||||
return nil, fmt.Errorf("shift count type %s, must be unsigned integer", yv.Kind.String())
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("shift count type %s, must be unsigned integer", yv.Kind.String())
|
||||
}
|
||||
|
||||
return xv.DwarfType, nil
|
||||
}
|
||||
|
||||
if xv.DwarfType == nil && yv.DwarfType == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if xv.DwarfType != nil && yv.DwarfType != nil {
|
||||
if xv.DwarfType.String() != yv.DwarfType.String() {
|
||||
return nil, fmt.Errorf("mismatched types \"%s\" and \"%s\"", xv.DwarfType.String(), yv.DwarfType.String())
|
||||
}
|
||||
return xv.DwarfType, nil
|
||||
} else if xv.DwarfType != nil && yv.DwarfType == nil {
|
||||
if err := yv.isType(xv.DwarfType, xv.Kind); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return xv.DwarfType, nil
|
||||
} else if xv.DwarfType == nil && yv.DwarfType != nil {
|
||||
if err := xv.isType(yv.DwarfType, yv.Kind); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return yv.DwarfType, nil
|
||||
}
|
||||
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
func (scope *EvalScope) evalBinary(node *ast.BinaryExpr) (*Variable, error) {
|
||||
switch node.Op {
|
||||
case token.INC, token.DEC, token.ARROW:
|
||||
return nil, fmt.Errorf("operator %s not supported", node.Op.String())
|
||||
}
|
||||
|
||||
xv, err := scope.evalAST(node.X)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
yv, err := scope.evalAST(node.Y)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
xv.loadValue()
|
||||
yv.loadValue()
|
||||
|
||||
if xv.Unreadable != nil {
|
||||
return nil, xv.Unreadable
|
||||
}
|
||||
|
||||
if yv.Unreadable != nil {
|
||||
return nil, yv.Unreadable
|
||||
}
|
||||
|
||||
typ, err := negotiateType(node.Op, xv, yv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
op := node.Op
|
||||
if typ != nil && (op == token.QUO) {
|
||||
_, isint := typ.(*dwarf.IntType)
|
||||
_, isuint := typ.(*dwarf.UintType)
|
||||
if isint || isuint {
|
||||
// forces integer division if the result type is integer
|
||||
op = token.QUO_ASSIGN
|
||||
}
|
||||
}
|
||||
|
||||
switch op {
|
||||
case token.EQL, token.LSS, token.GTR, token.NEQ, token.LEQ, token.GEQ:
|
||||
v, err := compareOp(op, xv, yv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newConstant(constant.MakeBool(v), xv.thread), nil
|
||||
|
||||
default:
|
||||
if xv.Value == nil {
|
||||
return nil, fmt.Errorf("operator %s can not be applied to \"%s\"", node.Op.String(), exprToString(node.X))
|
||||
}
|
||||
|
||||
if yv.Value == nil {
|
||||
return nil, fmt.Errorf("operator %s can not be applied to \"%s\"", node.Op.String(), exprToString(node.Y))
|
||||
}
|
||||
|
||||
rc, err := constantBinaryOp(op, xv.Value, yv.Value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if typ == nil {
|
||||
return newConstant(rc, xv.thread), nil
|
||||
} else {
|
||||
r := newVariable("", 0, typ, xv.thread)
|
||||
r.Value = rc
|
||||
return r, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Comapres xv to yv using operator op
|
||||
// Both xv and yv must be loaded and have a compatible type (as determined by negotiateType)
|
||||
func compareOp(op token.Token, xv *Variable, yv *Variable) (bool, error) {
|
||||
switch xv.Kind {
|
||||
case reflect.Bool:
|
||||
fallthrough
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
fallthrough
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
fallthrough
|
||||
case reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128:
|
||||
return constantCompare(op, xv.Value, yv.Value)
|
||||
case reflect.String:
|
||||
if int64(len(constant.StringVal(xv.Value))) != xv.Len || int64(len(constant.StringVal(yv.Value))) != yv.Len {
|
||||
return false, fmt.Errorf("string too long for comparison")
|
||||
}
|
||||
return constantCompare(op, xv.Value, yv.Value)
|
||||
}
|
||||
|
||||
if op != token.EQL && op != token.NEQ {
|
||||
return false, fmt.Errorf("operator %s not defined on %s", op.String(), xv.Kind.String())
|
||||
}
|
||||
|
||||
var eql bool
|
||||
var err error
|
||||
|
||||
switch xv.Kind {
|
||||
case reflect.Ptr:
|
||||
eql = xv.Children[0].Addr == yv.Children[0].Addr
|
||||
case reflect.Array:
|
||||
if int64(len(xv.Children)) != xv.Len || int64(len(yv.Children)) != yv.Len {
|
||||
return false, fmt.Errorf("array too long for comparison")
|
||||
}
|
||||
eql, err = equalChildren(xv, yv, true)
|
||||
case reflect.Struct:
|
||||
if len(xv.Children) != len(yv.Children) {
|
||||
return false, nil
|
||||
}
|
||||
if int64(len(xv.Children)) != xv.Len || int64(len(yv.Children)) != yv.Len {
|
||||
return false, fmt.Errorf("sturcture too deep for comparison")
|
||||
}
|
||||
eql, err = equalChildren(xv, yv, false)
|
||||
case reflect.Slice, reflect.Map, reflect.Func:
|
||||
if xv != nilVariable && yv != nilVariable {
|
||||
return false, fmt.Errorf("can not compare %s variables", xv.Kind.String())
|
||||
}
|
||||
|
||||
eql = xv.base == yv.base
|
||||
default:
|
||||
return false, fmt.Errorf("unimplemented comparison of %s variables", xv.Kind.String())
|
||||
}
|
||||
|
||||
if op == token.NEQ {
|
||||
return !eql, err
|
||||
}
|
||||
return eql, err
|
||||
}
|
||||
|
||||
func equalChildren(xv, yv *Variable, shortcircuit bool) (bool, error) {
|
||||
r := true
|
||||
for i := range xv.Children {
|
||||
eql, err := compareOp(token.EQL, &xv.Children[i], &yv.Children[i])
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
r = r && eql
|
||||
if !r && shortcircuit {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func (scope *EvalScope) findType(name string) (dwarf.Type, error) {
|
||||
reader := scope.DwarfReader()
|
||||
typentry, err := reader.SeekToTypeNamed(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return scope.Thread.dbp.dwarf.Type(typentry.Offset)
|
||||
}
|
||||
|
||||
func (v *Variable) asInt() (int64, error) {
|
||||
if v.DwarfType == nil {
|
||||
if v.Value.Kind() != constant.Int {
|
||||
return 0, fmt.Errorf("can not convert constant %s to int", v.Value)
|
||||
}
|
||||
} else {
|
||||
v.loadValue()
|
||||
if v.Unreadable != nil {
|
||||
return 0, v.Unreadable
|
||||
}
|
||||
if _, ok := v.DwarfType.(*dwarf.IntType); !ok {
|
||||
return 0, fmt.Errorf("can not convert value of type %s to int", v.DwarfType.String())
|
||||
}
|
||||
}
|
||||
n, _ := constant.Int64Val(v.Value)
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (v *Variable) isType(typ dwarf.Type, kind reflect.Kind) error {
|
||||
if v.DwarfType != nil {
|
||||
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 nil
|
||||
}
|
||||
|
||||
if typ == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if v == nilVariable {
|
||||
switch kind {
|
||||
case reflect.Slice, reflect.Map, reflect.Func, reflect.Ptr, reflect.Chan, reflect.Interface:
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("mismatched types nil and %s", typ.String())
|
||||
}
|
||||
}
|
||||
|
||||
converr := fmt.Errorf("can not convert %s constant to %s", v.Value, typ.String())
|
||||
|
||||
if v.Value == nil {
|
||||
return converr
|
||||
}
|
||||
|
||||
switch t := typ.(type) {
|
||||
case *dwarf.IntType:
|
||||
if v.Value.Kind() != constant.Int {
|
||||
return converr
|
||||
}
|
||||
case *dwarf.UintType:
|
||||
if v.Value.Kind() != constant.Int {
|
||||
return converr
|
||||
}
|
||||
case *dwarf.FloatType:
|
||||
if (v.Value.Kind() != constant.Int) && (v.Value.Kind() != constant.Float) {
|
||||
return converr
|
||||
}
|
||||
case *dwarf.BoolType:
|
||||
if v.Value.Kind() != constant.Bool {
|
||||
return converr
|
||||
}
|
||||
case *dwarf.StructType:
|
||||
if t.StructName != "string" {
|
||||
return converr
|
||||
}
|
||||
if v.Value.Kind() != constant.String {
|
||||
return converr
|
||||
}
|
||||
case *dwarf.ComplexType:
|
||||
if v.Value.Kind() != constant.Complex && v.Value.Kind() != constant.Float && v.Value.Kind() != constant.Int {
|
||||
return converr
|
||||
}
|
||||
default:
|
||||
return converr
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *Variable) sliceAccess(idx int) (*Variable, error) {
|
||||
if idx < 0 || int64(idx) >= v.Len {
|
||||
return nil, fmt.Errorf("index out of bounds")
|
||||
}
|
||||
return newVariable("", v.base+uintptr(int64(idx)*v.stride), v.fieldType, v.thread), nil
|
||||
}
|
||||
|
||||
func (v *Variable) reslice(low int64, high int64) (*Variable, error) {
|
||||
if low < 0 || low >= v.Len || high < 0 || high > v.Len {
|
||||
return nil, fmt.Errorf("index out of bounds")
|
||||
}
|
||||
|
||||
base := v.base + uintptr(int64(low)*v.stride)
|
||||
len := high - low
|
||||
|
||||
if high-low < 0 {
|
||||
return nil, fmt.Errorf("index out of bounds")
|
||||
}
|
||||
|
||||
typ := v.DwarfType
|
||||
if _, isarr := v.DwarfType.(*dwarf.ArrayType); isarr {
|
||||
typ = &dwarf.StructType{
|
||||
CommonType: dwarf.CommonType{
|
||||
ByteSize: 24,
|
||||
Name: "",
|
||||
},
|
||||
StructName: fmt.Sprintf("[]%s", v.fieldType),
|
||||
Kind: "struct",
|
||||
Field: nil,
|
||||
}
|
||||
}
|
||||
|
||||
r := newVariable("", 0, typ, v.thread)
|
||||
r.Cap = len
|
||||
r.Len = len
|
||||
r.base = base
|
||||
r.stride = v.stride
|
||||
r.fieldType = v.fieldType
|
||||
|
||||
return r, nil
|
||||
}
|
@ -10,7 +10,6 @@ import (
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
@ -854,8 +853,8 @@ func TestVariableEvaluation(t *testing.T) {
|
||||
{"baz", reflect.String, "bazburzum", 9, 0, 0},
|
||||
{"neg", reflect.Int, int64(-1), 0, 0, 0},
|
||||
{"f32", reflect.Float32, float64(float32(1.2)), 0, 0, 0},
|
||||
{"c64", reflect.Complex64, nil, 2, 0, 2},
|
||||
{"c128", reflect.Complex128, nil, 2, 0, 2},
|
||||
{"c64", reflect.Complex64, complex128(complex64(1 + 2i)), 0, 0, 0},
|
||||
{"c128", reflect.Complex128, complex128(2 + 3i), 0, 0, 0},
|
||||
{"a6.Baz", reflect.Int, int64(8), 0, 0, 0},
|
||||
{"a7.Baz", reflect.Int, int64(5), 0, 0, 0},
|
||||
{"a8.Baz", reflect.String, "feh", 3, 0, 0},
|
||||
@ -891,6 +890,12 @@ func TestVariableEvaluation(t *testing.T) {
|
||||
if y, ok := tc.value.(float64); !ok || x != y {
|
||||
t.Fatalf("%s value: expected: %v got: %v", tc.name, tc.value, v.Value)
|
||||
}
|
||||
case reflect.Complex64, reflect.Complex128:
|
||||
xr, _ := constant.Float64Val(constant.Real(v.Value))
|
||||
xi, _ := constant.Float64Val(constant.Imag(v.Value))
|
||||
if y, ok := tc.value.(complex128); !ok || complex(xr, xi) != y {
|
||||
t.Fatalf("%s value: expected: %v got: %v", tc.name, tc.value, v.Value)
|
||||
}
|
||||
case reflect.String:
|
||||
if y, ok := tc.value.(string); !ok || constant.StringVal(v.Value) != y {
|
||||
t.Fatalf("%s value: expected: %v got: %v", tc.name, tc.value, v.Value)
|
||||
@ -992,9 +997,9 @@ func TestPointerSetting(t *testing.T) {
|
||||
// change p1 to point to i2
|
||||
scope, err := p.CurrentThread.Scope()
|
||||
assertNoError(err, t, "Scope()")
|
||||
i2addr, err := scope.ExtractVariableInfo("i2")
|
||||
assertNoError(err, t, "EvalVariableAddr()")
|
||||
assertNoError(setVariable(p, "p1", strconv.Itoa(int(i2addr.Addr))), t, "SetVariable()")
|
||||
i2addr, err := scope.EvalExpression("i2")
|
||||
assertNoError(err, t, "EvalExpression()")
|
||||
assertNoError(setVariable(p, "p1", fmt.Sprintf("(*int)(0x%x)", i2addr.Addr)), t, "SetVariable()")
|
||||
pval(2)
|
||||
|
||||
// change the value of i2 check that p1 also changes
|
||||
|
@ -5,12 +5,10 @@ import (
|
||||
"debug/dwarf"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/constant"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
@ -30,6 +28,7 @@ const (
|
||||
// Represents a variable.
|
||||
type Variable struct {
|
||||
Addr uintptr
|
||||
OnlyAddr bool
|
||||
Name string
|
||||
DwarfType dwarf.Type
|
||||
RealType dwarf.Type
|
||||
@ -41,6 +40,9 @@ type Variable struct {
|
||||
Len int64
|
||||
Cap int64
|
||||
|
||||
// base address of arrays, base address of the backing array for slices (0 for nil slices)
|
||||
// base address of the backing byte array for strings
|
||||
// address of the function entry point for function variables (0 for nil function pointers)
|
||||
base uintptr
|
||||
stride int64
|
||||
fieldType dwarf.Type
|
||||
@ -125,6 +127,11 @@ func newVariable(name string, addr uintptr, dwarfType dwarf.Type, thread *Thread
|
||||
switch {
|
||||
case t.StructName == "string":
|
||||
v.Kind = reflect.String
|
||||
v.stride = 1
|
||||
v.fieldType = &dwarf.UintType{dwarf.BasicType{dwarf.CommonType{1, "byte"}, 8, 0}}
|
||||
if v.Addr != 0 {
|
||||
v.base, v.Len, v.Unreadable = v.thread.readStringInfo(v.Addr)
|
||||
}
|
||||
case strings.HasPrefix(t.StructName, "[]"):
|
||||
v.Kind = reflect.Slice
|
||||
if v.Addr != 0 {
|
||||
@ -177,6 +184,31 @@ func newVariable(name string, addr uintptr, dwarfType dwarf.Type, thread *Thread
|
||||
return v
|
||||
}
|
||||
|
||||
func newConstant(val constant.Value, thread *Thread) *Variable {
|
||||
v := &Variable{Value: val, thread: thread, loaded: true}
|
||||
switch val.Kind() {
|
||||
case constant.Int:
|
||||
v.Kind = reflect.Int
|
||||
case constant.Float:
|
||||
v.Kind = reflect.Float64
|
||||
case constant.Bool:
|
||||
v.Kind = reflect.Bool
|
||||
case constant.Complex:
|
||||
v.Kind = reflect.Complex128
|
||||
case constant.String:
|
||||
v.Kind = reflect.String
|
||||
v.Len = int64(len(constant.StringVal(val)))
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
var nilVariable = &Variable{
|
||||
Addr: 0,
|
||||
base: 0,
|
||||
Kind: reflect.Ptr,
|
||||
Children: []Variable{{Addr: 0, OnlyAddr: true}},
|
||||
}
|
||||
|
||||
func (v *Variable) clone() *Variable {
|
||||
r := *v
|
||||
return &r
|
||||
@ -395,62 +427,52 @@ func (g *G) Go() Location {
|
||||
return Location{PC: g.GoPC, File: f, Line: l, Fn: fn}
|
||||
}
|
||||
|
||||
// Returns information for the named variable.
|
||||
func (scope *EvalScope) ExtractVariableInfo(name string) (*Variable, error) {
|
||||
parts := strings.Split(name, ".")
|
||||
varName := parts[0]
|
||||
memberNames := parts[1:]
|
||||
|
||||
v, err := scope.extractVarInfo(varName)
|
||||
if err != nil {
|
||||
origErr := err
|
||||
// Attempt to evaluate name as a package variable.
|
||||
if len(memberNames) > 0 {
|
||||
v, err = scope.packageVarAddr(name)
|
||||
} else {
|
||||
_, _, fn := scope.Thread.dbp.PCToLine(scope.PC)
|
||||
if fn != nil {
|
||||
v, err = scope.packageVarAddr(fn.PackageName() + "." + name)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return nil, origErr
|
||||
}
|
||||
v.Name = name
|
||||
return v, nil
|
||||
} else {
|
||||
if len(memberNames) > 0 {
|
||||
for i := range memberNames {
|
||||
v, err = v.structMember(memberNames[i])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// Returns the value of the named variable.
|
||||
// Returns the value of the given expression (backwards compatibility).
|
||||
func (scope *EvalScope) EvalVariable(name string) (*Variable, error) {
|
||||
v, err := scope.ExtractVariableInfo(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v.loadValue()
|
||||
return v, nil
|
||||
return scope.EvalExpression(name)
|
||||
}
|
||||
|
||||
// Sets the value of the named variable
|
||||
func (scope *EvalScope) SetVariable(name, value string) error {
|
||||
v, err := scope.ExtractVariableInfo(name)
|
||||
t, err := parser.ParseExpr(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if v.Unreadable != nil {
|
||||
return fmt.Errorf("Variable \"%s\" is unreadable: %v\n", name, v.Unreadable)
|
||||
|
||||
xv, err := scope.evalAST(t)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return v.setValue(value)
|
||||
|
||||
if xv.Addr == 0 {
|
||||
return fmt.Errorf("Can not assign to \"%s\"", name)
|
||||
}
|
||||
|
||||
if xv.Unreadable != nil {
|
||||
return fmt.Errorf("Expression \"%s\" is unreadable: %v", name, xv.Unreadable)
|
||||
}
|
||||
|
||||
t, err = parser.ParseExpr(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
yv, err := scope.evalAST(t)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
yv.loadValue()
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
func (scope *EvalScope) extractVariableFromEntry(entry *dwarf.Entry) (*Variable, error) {
|
||||
@ -601,7 +623,7 @@ func (v *Variable) structMember(memberName string) (*Variable, error) {
|
||||
}
|
||||
return nil, fmt.Errorf("%s has no member %s", v.Name, memberName)
|
||||
default:
|
||||
return nil, fmt.Errorf("%s type %s is not a struct", v.Name, structVar.DwarfType)
|
||||
return nil, fmt.Errorf("%s (type %s) is not a struct", v.Name, structVar.DwarfType)
|
||||
}
|
||||
}
|
||||
|
||||
@ -670,7 +692,7 @@ func (v *Variable) loadValue() {
|
||||
}
|
||||
|
||||
func (v *Variable) loadValueInternal(recurseLevel int) {
|
||||
if v.Unreadable != nil || v.loaded || v.Addr == 0 {
|
||||
if v.Unreadable != nil || v.loaded || (v.Addr == 0 && v.base == 0) {
|
||||
return
|
||||
}
|
||||
v.loaded = true
|
||||
@ -683,7 +705,7 @@ func (v *Variable) loadValueInternal(recurseLevel int) {
|
||||
|
||||
case reflect.String:
|
||||
var val string
|
||||
val, v.Len, v.Unreadable = v.thread.readString(uintptr(v.Addr))
|
||||
val, v.Unreadable = v.thread.readStringValue(v.base, v.Len)
|
||||
v.Value = constant.MakeString(val)
|
||||
|
||||
case reflect.Slice, reflect.Array:
|
||||
@ -734,62 +756,87 @@ func (v *Variable) loadValueInternal(recurseLevel int) {
|
||||
}
|
||||
}
|
||||
|
||||
func (v *Variable) setValue(value string) error {
|
||||
switch t := v.RealType.(type) {
|
||||
case *dwarf.PtrType:
|
||||
return v.writeUint(false, value, int64(v.thread.dbp.arch.PtrSize()))
|
||||
case *dwarf.ComplexType:
|
||||
return v.writeComplex(value, t.ByteSize)
|
||||
case *dwarf.IntType:
|
||||
return v.writeUint(true, value, t.ByteSize)
|
||||
case *dwarf.UintType:
|
||||
return v.writeUint(false, value, t.ByteSize)
|
||||
case *dwarf.FloatType:
|
||||
return v.writeFloat(value, t.ByteSize)
|
||||
case *dwarf.BoolType:
|
||||
return v.writeBool(value)
|
||||
func (v *Variable) setValue(y *Variable) error {
|
||||
var err error
|
||||
switch v.Kind {
|
||||
case reflect.Float32, reflect.Float64:
|
||||
f, _ := constant.Float64Val(y.Value)
|
||||
err = v.writeFloatRaw(f, v.RealType.Size())
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
n, _ := constant.Int64Val(y.Value)
|
||||
err = v.writeUint(uint64(n), v.RealType.Size())
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
n, _ := constant.Uint64Val(y.Value)
|
||||
err = v.writeUint(n, v.RealType.Size())
|
||||
case reflect.Bool:
|
||||
err = v.writeBool(constant.BoolVal(y.Value))
|
||||
case reflect.Complex64, reflect.Complex128:
|
||||
real, _ := constant.Float64Val(constant.Real(y.Value))
|
||||
imag, _ := constant.Float64Val(constant.Imag(y.Value))
|
||||
err = v.writeComplex(real, imag, v.RealType.Size())
|
||||
default:
|
||||
return fmt.Errorf("Can not set value of variables of kind: %s\n", v.RealType.String())
|
||||
fmt.Printf("default\n")
|
||||
if _, isptr := v.RealType.(*dwarf.PtrType); isptr {
|
||||
err = v.writeUint(uint64(y.Children[0].Addr), int64(v.thread.dbp.arch.PtrSize()))
|
||||
} else {
|
||||
return fmt.Errorf("can not set variables of type %s (not implemented)", v.Kind.String())
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (thread *Thread) readString(addr uintptr) (string, int64, error) {
|
||||
func (thread *Thread) readStringInfo(addr uintptr) (uintptr, int64, error) {
|
||||
// string data structure is always two ptrs in size. Addr, followed by len
|
||||
// http://research.swtch.com/godata
|
||||
|
||||
// read len
|
||||
val, err := thread.readMemory(addr+uintptr(thread.dbp.arch.PtrSize()), thread.dbp.arch.PtrSize())
|
||||
if err != nil {
|
||||
return "", 0, fmt.Errorf("could not read string len %s", err)
|
||||
return 0, 0, fmt.Errorf("could not read string len %s", err)
|
||||
}
|
||||
strlen := int64(binary.LittleEndian.Uint64(val))
|
||||
if strlen < 0 {
|
||||
return "", 0, fmt.Errorf("invalid length: %d", strlen)
|
||||
}
|
||||
|
||||
count := strlen
|
||||
if count > maxArrayValues {
|
||||
count = maxArrayValues
|
||||
return 0, 0, fmt.Errorf("invalid length: %d", strlen)
|
||||
}
|
||||
|
||||
// read addr
|
||||
val, err = thread.readMemory(addr, thread.dbp.arch.PtrSize())
|
||||
if err != nil {
|
||||
return "", 0, fmt.Errorf("could not read string pointer %s", err)
|
||||
return 0, 0, fmt.Errorf("could not read string pointer %s", err)
|
||||
}
|
||||
addr = uintptr(binary.LittleEndian.Uint64(val))
|
||||
if addr == 0 {
|
||||
return "", 0, nil
|
||||
return 0, 0, nil
|
||||
}
|
||||
|
||||
val, err = thread.readMemory(addr, int(count))
|
||||
return addr, strlen, nil
|
||||
}
|
||||
|
||||
func (thread *Thread) readStringValue(addr uintptr, strlen int64) (string, error) {
|
||||
count := strlen
|
||||
if count > maxArrayValues {
|
||||
count = maxArrayValues
|
||||
}
|
||||
|
||||
val, err := thread.readMemory(addr, int(count))
|
||||
if err != nil {
|
||||
return "", 0, fmt.Errorf("could not read string at %#v due to %s", addr, err)
|
||||
return "", fmt.Errorf("could not read string at %#v due to %s", addr, err)
|
||||
}
|
||||
|
||||
retstr := *(*string)(unsafe.Pointer(&val))
|
||||
|
||||
return retstr, strlen, nil
|
||||
return retstr, nil
|
||||
}
|
||||
|
||||
func (thread *Thread) readString(addr uintptr) (string, int64, error) {
|
||||
addr, strlen, err := thread.readStringInfo(addr)
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
|
||||
retstr, err := thread.readStringValue(addr, strlen)
|
||||
return retstr, strlen, err
|
||||
}
|
||||
|
||||
func (v *Variable) loadSliceInfo(t *dwarf.StructType) {
|
||||
@ -883,78 +930,11 @@ func (v *Variable) readComplex(size int64) {
|
||||
imagvar := newVariable("imaginary", v.Addr+uintptr(fs), ftyp, v.thread)
|
||||
realvar.loadValue()
|
||||
imagvar.loadValue()
|
||||
v.Len = 2
|
||||
v.Children = []Variable{*realvar, *imagvar}
|
||||
v.Value = constant.BinaryOp(realvar.Value, token.ADD, imagvar.Value)
|
||||
v.Value = constant.BinaryOp(realvar.Value, token.ADD, constant.MakeImag(imagvar.Value))
|
||||
}
|
||||
|
||||
func (v *Variable) writeComplex(value string, size int64) error {
|
||||
var real, imag float64
|
||||
|
||||
expr, err := parser.ParseExpr(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var lits []*ast.BasicLit
|
||||
|
||||
if e, ok := expr.(*ast.ParenExpr); ok {
|
||||
expr = e.X
|
||||
}
|
||||
|
||||
switch e := expr.(type) {
|
||||
case *ast.BinaryExpr: // "<float> + <float>i" or "<float>i + <float>"
|
||||
x, xok := e.X.(*ast.BasicLit)
|
||||
y, yok := e.Y.(*ast.BasicLit)
|
||||
if e.Op != token.ADD || !xok || !yok {
|
||||
return fmt.Errorf("Not a complex constant: %s", value)
|
||||
}
|
||||
lits = []*ast.BasicLit{x, y}
|
||||
case *ast.CallExpr: // "complex(<float>, <float>)"
|
||||
tname, ok := e.Fun.(*ast.Ident)
|
||||
if !ok {
|
||||
return fmt.Errorf("Not a complex constant: %s", value)
|
||||
}
|
||||
if (tname.Name != "complex64") && (tname.Name != "complex128") {
|
||||
return fmt.Errorf("Not a complex constant: %s", value)
|
||||
}
|
||||
if len(e.Args) != 2 {
|
||||
return fmt.Errorf("Not a complex constant: %s", value)
|
||||
}
|
||||
for i := range e.Args {
|
||||
lit, ok := e.Args[i].(*ast.BasicLit)
|
||||
if !ok {
|
||||
return fmt.Errorf("Not a complex constant: %s", value)
|
||||
}
|
||||
lits = append(lits, lit)
|
||||
}
|
||||
lits[1].Kind = token.IMAG
|
||||
lits[1].Value = lits[1].Value + "i"
|
||||
case *ast.BasicLit: // "<float>" or "<float>i"
|
||||
lits = []*ast.BasicLit{e}
|
||||
default:
|
||||
return fmt.Errorf("Not a complex constant: %s", value)
|
||||
}
|
||||
|
||||
for _, lit := range lits {
|
||||
var err error
|
||||
var v float64
|
||||
switch lit.Kind {
|
||||
case token.FLOAT, token.INT:
|
||||
v, err = strconv.ParseFloat(lit.Value, int(size/2))
|
||||
real += v
|
||||
case token.IMAG:
|
||||
v, err = strconv.ParseFloat(lit.Value[:len(lit.Value)-1], int(size/2))
|
||||
imag += v
|
||||
default:
|
||||
return fmt.Errorf("Not a complex constant: %s", value)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = v.writeFloatRaw(real, int64(size/2))
|
||||
func (v *Variable) writeComplex(real, imag float64, size int64) error {
|
||||
err := v.writeFloatRaw(real, int64(size/2))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -985,36 +965,21 @@ func (thread *Thread) readIntRaw(addr uintptr, size int64) (int64, error) {
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (v *Variable) writeUint(signed bool, value string, size int64) error {
|
||||
var (
|
||||
n uint64
|
||||
err error
|
||||
)
|
||||
if signed {
|
||||
var m int64
|
||||
m, err = strconv.ParseInt(value, 0, int(size*8))
|
||||
n = uint64(m)
|
||||
} else {
|
||||
n, err = strconv.ParseUint(value, 0, int(size*8))
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
func (v *Variable) writeUint(value uint64, size int64) error {
|
||||
val := make([]byte, size)
|
||||
|
||||
switch size {
|
||||
case 1:
|
||||
val[0] = byte(n)
|
||||
val[0] = byte(value)
|
||||
case 2:
|
||||
binary.LittleEndian.PutUint16(val, uint16(n))
|
||||
binary.LittleEndian.PutUint16(val, uint16(value))
|
||||
case 4:
|
||||
binary.LittleEndian.PutUint32(val, uint32(n))
|
||||
binary.LittleEndian.PutUint32(val, uint32(value))
|
||||
case 8:
|
||||
binary.LittleEndian.PutUint64(val, uint64(n))
|
||||
binary.LittleEndian.PutUint64(val, uint64(value))
|
||||
}
|
||||
|
||||
_, err = v.thread.writeMemory(v.Addr, val)
|
||||
_, err := v.thread.writeMemory(v.Addr, val)
|
||||
return err
|
||||
}
|
||||
|
||||
@ -1061,14 +1026,6 @@ func (v *Variable) readFloatRaw(size int64) (float64, error) {
|
||||
return 0.0, fmt.Errorf("could not read float")
|
||||
}
|
||||
|
||||
func (v *Variable) writeFloat(value string, size int64) error {
|
||||
f, err := strconv.ParseFloat(value, int(size*8))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return v.writeFloatRaw(f, size)
|
||||
}
|
||||
|
||||
func (v *Variable) writeFloatRaw(f float64, size int64) error {
|
||||
buf := bytes.NewBuffer(make([]byte, 0, size))
|
||||
|
||||
@ -1085,16 +1042,10 @@ func (v *Variable) writeFloatRaw(f float64, size int64) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func (v *Variable) writeBool(value string) error {
|
||||
b, err := strconv.ParseBool(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
func (v *Variable) writeBool(value bool) error {
|
||||
val := []byte{0}
|
||||
if b {
|
||||
val[0] = *(*byte)(unsafe.Pointer(&b))
|
||||
}
|
||||
_, err = v.thread.writeMemory(v.Addr, val)
|
||||
val[0] = *(*byte)(unsafe.Pointer(&value))
|
||||
_, err := v.thread.writeMemory(v.Addr, val)
|
||||
return err
|
||||
}
|
||||
|
||||
@ -1108,7 +1059,8 @@ func (v *Variable) readFunctionPtr() {
|
||||
// dereference pointer to find function pc
|
||||
fnaddr := uintptr(binary.LittleEndian.Uint64(val))
|
||||
if fnaddr == 0 {
|
||||
v.Unreadable = err
|
||||
v.base = 0
|
||||
v.Value = constant.MakeString("")
|
||||
return
|
||||
}
|
||||
|
||||
@ -1118,10 +1070,10 @@ func (v *Variable) readFunctionPtr() {
|
||||
return
|
||||
}
|
||||
|
||||
funcAddr := binary.LittleEndian.Uint64(val)
|
||||
fn := v.thread.dbp.goSymTable.PCToFunc(uint64(funcAddr))
|
||||
v.base = uintptr(binary.LittleEndian.Uint64(val))
|
||||
fn := v.thread.dbp.goSymTable.PCToFunc(uint64(v.base))
|
||||
if fn == nil {
|
||||
v.Unreadable = fmt.Errorf("could not find function for %#v", funcAddr)
|
||||
v.Unreadable = fmt.Errorf("could not find function for %#v", v.base)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -58,11 +58,12 @@ func ConvertThread(th *proc.Thread) *Thread {
|
||||
|
||||
func ConvertVar(v *proc.Variable) *Variable {
|
||||
r := Variable{
|
||||
Addr: v.Addr,
|
||||
Name: v.Name,
|
||||
Kind: v.Kind,
|
||||
Len: v.Len,
|
||||
Cap: v.Cap,
|
||||
Addr: v.Addr,
|
||||
OnlyAddr: v.OnlyAddr,
|
||||
Name: v.Name,
|
||||
Kind: v.Kind,
|
||||
Len: v.Len,
|
||||
Cap: v.Cap,
|
||||
}
|
||||
|
||||
if v.DwarfType != nil {
|
||||
@ -92,10 +93,42 @@ func ConvertVar(v *proc.Variable) *Variable {
|
||||
}
|
||||
}
|
||||
|
||||
r.Children = make([]Variable, len(v.Children))
|
||||
switch v.Kind {
|
||||
case reflect.Complex64:
|
||||
r.Children = make([]Variable, 2)
|
||||
r.Len = 2
|
||||
|
||||
for i := range v.Children {
|
||||
r.Children[i] = *ConvertVar(&v.Children[i])
|
||||
real, _ := constant.Float64Val(constant.Real(v.Value))
|
||||
imag, _ := constant.Float64Val(constant.Imag(v.Value))
|
||||
|
||||
r.Children[0].Name = "real"
|
||||
r.Children[0].Kind = reflect.Float32
|
||||
r.Children[0].Value = strconv.FormatFloat(real, 'f', -1, 32)
|
||||
|
||||
r.Children[1].Name = "imaginary"
|
||||
r.Children[1].Kind = reflect.Float32
|
||||
r.Children[1].Value = strconv.FormatFloat(imag, 'f', -1, 32)
|
||||
case reflect.Complex128:
|
||||
r.Children = make([]Variable, 2)
|
||||
r.Len = 2
|
||||
|
||||
real, _ := constant.Float64Val(constant.Real(v.Value))
|
||||
imag, _ := constant.Float64Val(constant.Imag(v.Value))
|
||||
|
||||
r.Children[0].Name = "real"
|
||||
r.Children[0].Kind = reflect.Float64
|
||||
r.Children[0].Value = strconv.FormatFloat(real, 'f', -1, 64)
|
||||
|
||||
r.Children[1].Name = "imaginary"
|
||||
r.Children[1].Kind = reflect.Float64
|
||||
r.Children[1].Value = strconv.FormatFloat(imag, 'f', -1, 64)
|
||||
|
||||
default:
|
||||
r.Children = make([]Variable, len(v.Children))
|
||||
|
||||
for i := range v.Children {
|
||||
r.Children[i] = *ConvertVar(&v.Children[i])
|
||||
}
|
||||
}
|
||||
|
||||
return &r
|
||||
|
@ -16,7 +16,7 @@ const (
|
||||
// Returns a representation of v on a single line
|
||||
func (v *Variable) SinglelineString() string {
|
||||
var buf bytes.Buffer
|
||||
v.writeTo(&buf, false, true, "")
|
||||
v.writeTo(&buf, true, false, true, "")
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
@ -24,17 +24,17 @@ func (v *Variable) SinglelineString() string {
|
||||
func (v *Variable) MultilineString(indent string) string {
|
||||
var buf bytes.Buffer
|
||||
buf.WriteString(indent)
|
||||
v.writeTo(&buf, true, true, indent)
|
||||
v.writeTo(&buf, true, true, true, indent)
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func (v *Variable) writeTo(buf *bytes.Buffer, newlines, includeType bool, indent string) {
|
||||
func (v *Variable) writeTo(buf *bytes.Buffer, top, newlines, includeType bool, indent string) {
|
||||
if v.Unreadable != "" {
|
||||
fmt.Fprintf(buf, "(unreadable %s)", v.Unreadable)
|
||||
return
|
||||
}
|
||||
|
||||
if v.Addr == 0 {
|
||||
if !top && v.Addr == 0 {
|
||||
fmt.Fprintf(buf, "%s nil", v.Type)
|
||||
return
|
||||
}
|
||||
@ -45,8 +45,14 @@ func (v *Variable) writeTo(buf *bytes.Buffer, newlines, includeType bool, indent
|
||||
case reflect.Array:
|
||||
v.writeArrayTo(buf, newlines, includeType, indent)
|
||||
case reflect.Ptr:
|
||||
fmt.Fprintf(buf, "*")
|
||||
v.Children[0].writeTo(buf, newlines, includeType, indent)
|
||||
if v.Type == "" {
|
||||
fmt.Fprintf(buf, "nil")
|
||||
} else if v.Children[0].OnlyAddr {
|
||||
fmt.Fprintf(buf, "(%s)(0x%x)", v.Type, v.Children[0].Addr)
|
||||
} else {
|
||||
fmt.Fprintf(buf, "*")
|
||||
v.Children[0].writeTo(buf, false, newlines, includeType, indent)
|
||||
}
|
||||
case reflect.String:
|
||||
v.writeStringTo(buf)
|
||||
case reflect.Struct:
|
||||
@ -54,14 +60,13 @@ func (v *Variable) writeTo(buf *bytes.Buffer, newlines, includeType bool, indent
|
||||
case reflect.Map:
|
||||
v.writeMapTo(buf, newlines, includeType, indent)
|
||||
case reflect.Func:
|
||||
fmt.Fprintf(buf, "%s", v.Value)
|
||||
case reflect.Complex64, reflect.Complex128:
|
||||
switch v.RealType {
|
||||
case "complex64":
|
||||
fmt.Fprintf(buf, "(%s + %si)", v.Children[0].Value, v.Children[1].Value)
|
||||
case "complex128":
|
||||
fmt.Fprintf(buf, "(%s + %si)", v.Children[0].Value, v.Children[1].Value)
|
||||
if v.Value == "" {
|
||||
fmt.Fprintf(buf, "nil")
|
||||
} else {
|
||||
fmt.Fprintf(buf, "%s", v.Value)
|
||||
}
|
||||
case reflect.Complex64, reflect.Complex128:
|
||||
fmt.Fprintf(buf, "(%s + %si)", v.Children[0].Value, v.Children[1].Value)
|
||||
default:
|
||||
if v.Value != "" {
|
||||
buf.Write([]byte(v.Value))
|
||||
@ -112,7 +117,7 @@ func (v *Variable) writeStructTo(buf *bytes.Buffer, newlines, includeType bool,
|
||||
fmt.Fprintf(buf, "\n%s%s", indent, indentString)
|
||||
}
|
||||
fmt.Fprintf(buf, "%s: ", v.Children[i].Name)
|
||||
v.Children[i].writeTo(buf, nl, true, indent+indentString)
|
||||
v.Children[i].writeTo(buf, false, nl, true, indent+indentString)
|
||||
if i != len(v.Children)-1 || nl {
|
||||
fmt.Fprintf(buf, ",")
|
||||
if !nl {
|
||||
@ -144,9 +149,9 @@ func (v *Variable) writeMapTo(buf *bytes.Buffer, newlines, includeType bool, ind
|
||||
fmt.Fprintf(buf, "\n%s%s", indent, indentString)
|
||||
}
|
||||
|
||||
key.writeTo(buf, false, false, indent+indentString)
|
||||
key.writeTo(buf, false, false, false, indent+indentString)
|
||||
fmt.Fprintf(buf, ": ")
|
||||
value.writeTo(buf, nl, false, indent+indentString)
|
||||
value.writeTo(buf, false, nl, false, indent+indentString)
|
||||
if i != len(v.Children)-1 || nl {
|
||||
fmt.Fprintf(buf, ", ")
|
||||
}
|
||||
@ -239,7 +244,7 @@ func (v *Variable) writeSliceOrArrayTo(buf *bytes.Buffer, newlines bool, indent
|
||||
if nl {
|
||||
fmt.Fprintf(buf, "\n%s%s", indent, indentString)
|
||||
}
|
||||
v.Children[i].writeTo(buf, nl, false, indent+indentString)
|
||||
v.Children[i].writeTo(buf, false, nl, false, indent+indentString)
|
||||
if i != len(v.Children)-1 || nl {
|
||||
fmt.Fprintf(buf, ",")
|
||||
}
|
||||
|
@ -110,6 +110,8 @@ type Variable struct {
|
||||
Name string `json:"name"`
|
||||
// Address of the variable or struct member
|
||||
Addr uintptr `json:"addr"`
|
||||
// Only the address field is filled (result of evaluating expressions like &<expr>)
|
||||
OnlyAddr bool `json:"onlyAddr"`
|
||||
// Go type of the variable
|
||||
Type string `json:"type"`
|
||||
// Type of the variable after resolving any typedefs
|
||||
|
@ -13,11 +13,12 @@ import (
|
||||
)
|
||||
|
||||
type varTest struct {
|
||||
name string
|
||||
value string
|
||||
setTo string
|
||||
varType string
|
||||
err error
|
||||
name string
|
||||
preserveName bool
|
||||
value string
|
||||
setTo string
|
||||
varType string
|
||||
err error
|
||||
}
|
||||
|
||||
func matchStringOrPrefix(output, target string) bool {
|
||||
@ -28,12 +29,13 @@ func matchStringOrPrefix(output, target string) bool {
|
||||
} else {
|
||||
return output == target
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func assertVariable(t *testing.T, variable *proc.Variable, expected varTest) {
|
||||
if variable.Name != expected.name {
|
||||
t.Fatalf("Expected %s got %s\n", expected.name, variable.Name)
|
||||
if expected.preserveName {
|
||||
if variable.Name != expected.name {
|
||||
t.Fatalf("Expected %s got %s\n", expected.name, variable.Name)
|
||||
}
|
||||
}
|
||||
|
||||
cv := api.ConvertVar(variable)
|
||||
@ -92,47 +94,47 @@ func withTestProcess(name string, t *testing.T, fn func(p *proc.Process, fixture
|
||||
|
||||
func TestVariableEvaluation(t *testing.T) {
|
||||
testcases := []varTest{
|
||||
{"a1", "\"foofoofoofoofoofoo\"", "", "struct string", nil},
|
||||
{"a11", "[3]main.FooBar [{Baz: 1, Bur: \"a\"},{Baz: 2, Bur: \"b\"},{Baz: 3, Bur: \"c\"}]", "", "[3]main.FooBar", nil},
|
||||
{"a12", "[]main.FooBar len: 2, cap: 2, [{Baz: 4, Bur: \"d\"},{Baz: 5, Bur: \"e\"}]", "", "struct []main.FooBar", nil},
|
||||
{"a13", "[]*main.FooBar len: 3, cap: 3, [*{Baz: 6, Bur: \"f\"},*{Baz: 7, Bur: \"g\"},*{Baz: 8, Bur: \"h\"}]", "", "struct []*main.FooBar", nil},
|
||||
{"a2", "6", "10", "int", nil},
|
||||
{"a3", "7.23", "3.1", "float64", nil},
|
||||
{"a4", "[2]int [1,2]", "", "[2]int", nil},
|
||||
{"a5", "[]int len: 5, cap: 5, [1,2,3,4,5]", "", "struct []int", nil},
|
||||
{"a6", "main.FooBar {Baz: 8, Bur: \"word\"}", "", "main.FooBar", nil},
|
||||
{"a7", "*main.FooBar {Baz: 5, Bur: \"strum\"}", "", "*main.FooBar", nil},
|
||||
{"a8", "main.FooBar2 {Bur: 10, Baz: \"feh\"}", "", "main.FooBar2", nil},
|
||||
{"a9", "*main.FooBar nil", "", "*main.FooBar", nil},
|
||||
{"baz", "\"bazburzum\"", "", "struct string", nil},
|
||||
{"neg", "-1", "-20", "int", nil},
|
||||
{"f32", "1.2", "1.1", "float32", nil},
|
||||
{"c64", "(1 + 2i)", "(4 + 5i)", "complex64", nil},
|
||||
{"c128", "(2 + 3i)", "(6.3 + 7i)", "complex128", nil},
|
||||
{"a6.Baz", "8", "20", "int", nil},
|
||||
{"a7.Baz", "5", "25", "int", nil},
|
||||
{"a8.Baz", "\"feh\"", "", "struct string", nil},
|
||||
{"a9.Baz", "nil", "", "int", fmt.Errorf("a9 is nil")},
|
||||
{"a9.NonExistent", "nil", "", "int", fmt.Errorf("a9 has no member NonExistent")},
|
||||
{"a8", "main.FooBar2 {Bur: 10, Baz: \"feh\"}", "", "main.FooBar2", nil}, // reread variable after member
|
||||
{"i32", "[2]int32 [1,2]", "", "[2]int32", nil},
|
||||
{"b1", "true", "false", "bool", nil},
|
||||
{"b2", "false", "true", "bool", nil},
|
||||
{"i8", "1", "2", "int8", nil},
|
||||
{"u16", "65535", "0", "uint16", nil},
|
||||
{"u32", "4294967295", "1", "uint32", nil},
|
||||
{"u64", "18446744073709551615", "2", "uint64", nil},
|
||||
{"u8", "255", "3", "uint8", nil},
|
||||
{"up", "5", "4", "uintptr", nil},
|
||||
{"f", "main.barfoo", "", "func()", nil},
|
||||
{"ba", "[]int len: 200, cap: 200, [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,...+136 more]", "", "struct []int", nil},
|
||||
{"ms", "main.Nest {Level: 0, Nest: *main.Nest {Level: 1, Nest: *(*main.Nest)(…", "", "main.Nest", nil},
|
||||
{"ms.Nest.Nest", "*main.Nest {Level: 2, Nest: *main.Nest {Level: 3, Nest: *(*main.Nest)(…", "", "*main.Nest", nil},
|
||||
{"ms.Nest.Nest.Nest.Nest.Nest", "*main.Nest nil", "", "*main.Nest", nil},
|
||||
{"ms.Nest.Nest.Nest.Nest.Nest.Nest", "", "", "*main.Nest", fmt.Errorf("ms.Nest.Nest.Nest.Nest.Nest is nil")},
|
||||
{"main.p1", "10", "12", "int", nil},
|
||||
{"p1", "10", "13", "int", nil},
|
||||
{"NonExistent", "", "", "", fmt.Errorf("could not find symbol value for NonExistent")},
|
||||
{"a1", true, "\"foofoofoofoofoofoo\"", "", "struct string", nil},
|
||||
{"a11", true, "[3]main.FooBar [{Baz: 1, Bur: \"a\"},{Baz: 2, Bur: \"b\"},{Baz: 3, Bur: \"c\"}]", "", "[3]main.FooBar", nil},
|
||||
{"a12", true, "[]main.FooBar len: 2, cap: 2, [{Baz: 4, Bur: \"d\"},{Baz: 5, Bur: \"e\"}]", "", "struct []main.FooBar", nil},
|
||||
{"a13", true, "[]*main.FooBar len: 3, cap: 3, [*{Baz: 6, Bur: \"f\"},*{Baz: 7, Bur: \"g\"},*{Baz: 8, Bur: \"h\"}]", "", "struct []*main.FooBar", nil},
|
||||
{"a2", true, "6", "10", "int", nil},
|
||||
{"a3", true, "7.23", "3.1", "float64", nil},
|
||||
{"a4", true, "[2]int [1,2]", "", "[2]int", nil},
|
||||
{"a5", true, "[]int len: 5, cap: 5, [1,2,3,4,5]", "", "struct []int", nil},
|
||||
{"a6", true, "main.FooBar {Baz: 8, Bur: \"word\"}", "", "main.FooBar", nil},
|
||||
{"a7", true, "*main.FooBar {Baz: 5, Bur: \"strum\"}", "", "*main.FooBar", nil},
|
||||
{"a8", true, "main.FooBar2 {Bur: 10, Baz: \"feh\"}", "", "main.FooBar2", nil},
|
||||
{"a9", true, "*main.FooBar nil", "", "*main.FooBar", nil},
|
||||
{"baz", true, "\"bazburzum\"", "", "struct string", nil},
|
||||
{"neg", true, "-1", "-20", "int", nil},
|
||||
{"f32", true, "1.2", "1.1", "float32", nil},
|
||||
{"c64", true, "(1 + 2i)", "(4 + 5i)", "complex64", nil},
|
||||
{"c128", true, "(2 + 3i)", "(6.3 + 7i)", "complex128", nil},
|
||||
{"a6.Baz", true, "8", "20", "int", nil},
|
||||
{"a7.Baz", true, "5", "25", "int", nil},
|
||||
{"a8.Baz", true, "\"feh\"", "", "struct string", nil},
|
||||
{"a9.Baz", true, "nil", "", "int", fmt.Errorf("a9 is nil")},
|
||||
{"a9.NonExistent", true, "nil", "", "int", fmt.Errorf("a9 has no member NonExistent")},
|
||||
{"a8", true, "main.FooBar2 {Bur: 10, Baz: \"feh\"}", "", "main.FooBar2", nil}, // reread variable after member
|
||||
{"i32", true, "[2]int32 [1,2]", "", "[2]int32", nil},
|
||||
{"b1", true, "true", "false", "bool", nil},
|
||||
{"b2", true, "false", "true", "bool", nil},
|
||||
{"i8", true, "1", "2", "int8", nil},
|
||||
{"u16", true, "65535", "0", "uint16", nil},
|
||||
{"u32", true, "4294967295", "1", "uint32", nil},
|
||||
{"u64", true, "18446744073709551615", "2", "uint64", nil},
|
||||
{"u8", true, "255", "3", "uint8", nil},
|
||||
{"up", true, "5", "4", "uintptr", nil},
|
||||
{"f", true, "main.barfoo", "", "func()", nil},
|
||||
{"ba", true, "[]int len: 200, cap: 200, [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,...+136 more]", "", "struct []int", nil},
|
||||
{"ms", true, "main.Nest {Level: 0, Nest: *main.Nest {Level: 1, Nest: *(*main.Nest)(…", "", "main.Nest", nil},
|
||||
{"ms.Nest.Nest", true, "*main.Nest {Level: 2, Nest: *main.Nest {Level: 3, Nest: *(*main.Nest)(…", "", "*main.Nest", nil},
|
||||
{"ms.Nest.Nest.Nest.Nest.Nest", true, "*main.Nest nil", "", "*main.Nest", nil},
|
||||
{"ms.Nest.Nest.Nest.Nest.Nest.Nest", true, "", "", "*main.Nest", fmt.Errorf("ms.Nest.Nest.Nest.Nest.Nest is nil")},
|
||||
{"main.p1", true, "10", "12", "int", nil},
|
||||
{"p1", true, "10", "13", "int", nil},
|
||||
{"NonExistent", true, "", "", "", fmt.Errorf("could not find symbol value for NonExistent")},
|
||||
}
|
||||
|
||||
withTestProcess("testvariables", t, func(p *proc.Process, fixture protest.Fixture) {
|
||||
@ -170,32 +172,32 @@ func TestVariableEvaluation(t *testing.T) {
|
||||
|
||||
func TestMultilineVariableEvaluation(t *testing.T) {
|
||||
testcases := []varTest{
|
||||
{"a1", "\"foofoofoofoofoofoo\"", "", "struct string", nil},
|
||||
{"a11", `[3]main.FooBar [
|
||||
{"a1", true, "\"foofoofoofoofoofoo\"", "", "struct string", nil},
|
||||
{"a11", true, `[3]main.FooBar [
|
||||
{Baz: 1, Bur: "a"},
|
||||
{Baz: 2, Bur: "b"},
|
||||
{Baz: 3, Bur: "c"},
|
||||
]`, "", "[3]main.FooBar", nil},
|
||||
{"a12", `[]main.FooBar len: 2, cap: 2, [
|
||||
{"a12", true, `[]main.FooBar len: 2, cap: 2, [
|
||||
{Baz: 4, Bur: "d"},
|
||||
{Baz: 5, Bur: "e"},
|
||||
]`, "", "struct []main.FooBar", nil},
|
||||
{"a13", `[]*main.FooBar len: 3, cap: 3, [
|
||||
{"a13", true, `[]*main.FooBar len: 3, cap: 3, [
|
||||
*{Baz: 6, Bur: "f"},
|
||||
*{Baz: 7, Bur: "g"},
|
||||
*{Baz: 8, Bur: "h"},
|
||||
]`, "", "struct []*main.FooBar", nil},
|
||||
{"a2", "6", "10", "int", nil},
|
||||
{"a4", "[2]int [1,2]", "", "[2]int", nil},
|
||||
{"a5", "[]int len: 5, cap: 5, [1,2,3,4,5]", "", "struct []int", nil},
|
||||
{"a6", "main.FooBar {Baz: 8, Bur: \"word\"}", "", "main.FooBar", nil},
|
||||
{"a7", "*main.FooBar {Baz: 5, Bur: \"strum\"}", "", "*main.FooBar", nil},
|
||||
{"a8", "main.FooBar2 {Bur: 10, Baz: \"feh\"}", "", "main.FooBar2", nil},
|
||||
{"a9", "*main.FooBar nil", "", "*main.FooBar", nil},
|
||||
{"a8", "main.FooBar2 {Bur: 10, Baz: \"feh\"}", "", "main.FooBar2", nil}, // reread variable after member
|
||||
{"i32", "[2]int32 [1,2]", "", "[2]int32", nil},
|
||||
{"ba", "[]int len: 200, cap: 200, [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,...+136 more]", "", "struct []int", nil},
|
||||
{"ms", `main.Nest {
|
||||
{"a2", true, "6", "10", "int", nil},
|
||||
{"a4", true, "[2]int [1,2]", "", "[2]int", nil},
|
||||
{"a5", true, "[]int len: 5, cap: 5, [1,2,3,4,5]", "", "struct []int", nil},
|
||||
{"a6", true, "main.FooBar {Baz: 8, Bur: \"word\"}", "", "main.FooBar", nil},
|
||||
{"a7", true, "*main.FooBar {Baz: 5, Bur: \"strum\"}", "", "*main.FooBar", nil},
|
||||
{"a8", true, "main.FooBar2 {Bur: 10, Baz: \"feh\"}", "", "main.FooBar2", nil},
|
||||
{"a9", true, "*main.FooBar nil", "", "*main.FooBar", nil},
|
||||
{"a8", true, "main.FooBar2 {Bur: 10, Baz: \"feh\"}", "", "main.FooBar2", nil}, // reread variable after member
|
||||
{"i32", true, "[2]int32 [1,2]", "", "[2]int32", nil},
|
||||
{"ba", true, "[]int len: 200, cap: 200, [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,...+136 more]", "", "struct []int", nil},
|
||||
{"ms", true, `main.Nest {
|
||||
Level: 0,
|
||||
Nest: *main.Nest {
|
||||
Level: 1,
|
||||
@ -240,39 +242,39 @@ func TestLocalVariables(t *testing.T) {
|
||||
}{
|
||||
{(*proc.EvalScope).LocalVariables,
|
||||
[]varTest{
|
||||
{"a1", "\"foofoofoofoofoofoo\"", "", "struct string", nil},
|
||||
{"a10", "\"ofo\"", "", "struct string", nil},
|
||||
{"a11", "[3]main.FooBar [{Baz: 1, Bur: \"a\"},{Baz: 2, Bur: \"b\"},{Baz: 3, Bur: \"c\"}]", "", "[3]main.FooBar", nil},
|
||||
{"a12", "[]main.FooBar len: 2, cap: 2, [{Baz: 4, Bur: \"d\"},{Baz: 5, Bur: \"e\"}]", "", "struct []main.FooBar", nil},
|
||||
{"a13", "[]*main.FooBar len: 3, cap: 3, [*{Baz: 6, Bur: \"f\"},*{Baz: 7, Bur: \"g\"},*{Baz: 8, Bur: \"h\"}]", "", "struct []*main.FooBar", nil},
|
||||
{"a2", "6", "", "int", nil},
|
||||
{"a3", "7.23", "", "float64", nil},
|
||||
{"a4", "[2]int [1,2]", "", "[2]int", nil},
|
||||
{"a5", "[]int len: 5, cap: 5, [1,2,3,4,5]", "", "struct []int", nil},
|
||||
{"a6", "main.FooBar {Baz: 8, Bur: \"word\"}", "", "main.FooBar", nil},
|
||||
{"a7", "*main.FooBar {Baz: 5, Bur: \"strum\"}", "", "*main.FooBar", nil},
|
||||
{"a8", "main.FooBar2 {Bur: 10, Baz: \"feh\"}", "", "main.FooBar2", nil},
|
||||
{"a9", "*main.FooBar nil", "", "*main.FooBar", nil},
|
||||
{"b1", "true", "", "bool", nil},
|
||||
{"b2", "false", "", "bool", nil},
|
||||
{"ba", "[]int len: 200, cap: 200, [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,...+136 more]", "", "struct []int", nil},
|
||||
{"c128", "(2 + 3i)", "", "complex128", nil},
|
||||
{"c64", "(1 + 2i)", "", "complex64", nil},
|
||||
{"f", "main.barfoo", "", "func()", nil},
|
||||
{"f32", "1.2", "", "float32", nil},
|
||||
{"i32", "[2]int32 [1,2]", "", "[2]int32", nil},
|
||||
{"i8", "1", "", "int8", nil},
|
||||
{"ms", "main.Nest {Level: 0, Nest: *main.Nest {Level: 1, Nest: *(*main.Nest)…", "", "main.Nest", nil},
|
||||
{"neg", "-1", "", "int", nil},
|
||||
{"u16", "65535", "", "uint16", nil},
|
||||
{"u32", "4294967295", "", "uint32", nil},
|
||||
{"u64", "18446744073709551615", "", "uint64", nil},
|
||||
{"u8", "255", "", "uint8", nil},
|
||||
{"up", "5", "", "uintptr", nil}}},
|
||||
{"a1", true, "\"foofoofoofoofoofoo\"", "", "struct string", nil},
|
||||
{"a10", true, "\"ofo\"", "", "struct string", nil},
|
||||
{"a11", true, "[3]main.FooBar [{Baz: 1, Bur: \"a\"},{Baz: 2, Bur: \"b\"},{Baz: 3, Bur: \"c\"}]", "", "[3]main.FooBar", nil},
|
||||
{"a12", true, "[]main.FooBar len: 2, cap: 2, [{Baz: 4, Bur: \"d\"},{Baz: 5, Bur: \"e\"}]", "", "struct []main.FooBar", nil},
|
||||
{"a13", true, "[]*main.FooBar len: 3, cap: 3, [*{Baz: 6, Bur: \"f\"},*{Baz: 7, Bur: \"g\"},*{Baz: 8, Bur: \"h\"}]", "", "struct []*main.FooBar", nil},
|
||||
{"a2", true, "6", "", "int", nil},
|
||||
{"a3", true, "7.23", "", "float64", nil},
|
||||
{"a4", true, "[2]int [1,2]", "", "[2]int", nil},
|
||||
{"a5", true, "[]int len: 5, cap: 5, [1,2,3,4,5]", "", "struct []int", nil},
|
||||
{"a6", true, "main.FooBar {Baz: 8, Bur: \"word\"}", "", "main.FooBar", nil},
|
||||
{"a7", true, "*main.FooBar {Baz: 5, Bur: \"strum\"}", "", "*main.FooBar", nil},
|
||||
{"a8", true, "main.FooBar2 {Bur: 10, Baz: \"feh\"}", "", "main.FooBar2", nil},
|
||||
{"a9", true, "*main.FooBar nil", "", "*main.FooBar", nil},
|
||||
{"b1", true, "true", "", "bool", nil},
|
||||
{"b2", true, "false", "", "bool", nil},
|
||||
{"ba", true, "[]int len: 200, cap: 200, [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,...+136 more]", "", "struct []int", nil},
|
||||
{"c128", true, "(2 + 3i)", "", "complex128", nil},
|
||||
{"c64", true, "(1 + 2i)", "", "complex64", nil},
|
||||
{"f", true, "main.barfoo", "", "func()", nil},
|
||||
{"f32", true, "1.2", "", "float32", nil},
|
||||
{"i32", true, "[2]int32 [1,2]", "", "[2]int32", nil},
|
||||
{"i8", true, "1", "", "int8", nil},
|
||||
{"ms", true, "main.Nest {Level: 0, Nest: *main.Nest {Level: 1, Nest: *(*main.Nest)…", "", "main.Nest", nil},
|
||||
{"neg", true, "-1", "", "int", nil},
|
||||
{"u16", true, "65535", "", "uint16", nil},
|
||||
{"u32", true, "4294967295", "", "uint32", nil},
|
||||
{"u64", true, "18446744073709551615", "", "uint64", nil},
|
||||
{"u8", true, "255", "", "uint8", nil},
|
||||
{"up", true, "5", "", "uintptr", nil}}},
|
||||
{(*proc.EvalScope).FunctionArguments,
|
||||
[]varTest{
|
||||
{"bar", "main.FooBar {Baz: 10, Bur: \"lorem\"}", "", "main.FooBar", nil},
|
||||
{"baz", "\"bazburzum\"", "", "struct string", nil}}},
|
||||
{"bar", true, "main.FooBar {Baz: 10, Bur: \"lorem\"}", "", "main.FooBar", nil},
|
||||
{"baz", true, "\"bazburzum\"", "", "struct string", nil}}},
|
||||
}
|
||||
|
||||
withTestProcess("testvariables", t, func(p *proc.Process, fixture protest.Fixture) {
|
||||
@ -301,12 +303,13 @@ func TestLocalVariables(t *testing.T) {
|
||||
func TestEmbeddedStruct(t *testing.T) {
|
||||
withTestProcess("testvariables4", t, func(p *proc.Process, fixture protest.Fixture) {
|
||||
testcases := []varTest{
|
||||
{"b.val", "-314", "", "int", nil},
|
||||
{"b.A.val", "-314", "", "int", nil},
|
||||
{"b.a.val", "42", "", "int", nil},
|
||||
{"b.ptr.val", "1337", "", "int", nil},
|
||||
{"b.C.s", "\"hello\"", "", "struct string", nil},
|
||||
{"b.s", "\"hello\"", "", "struct string", nil},
|
||||
{"b.val", true, "-314", "", "int", nil},
|
||||
{"b.A.val", true, "-314", "", "int", nil},
|
||||
{"b.a.val", true, "42", "", "int", nil},
|
||||
{"b.ptr.val", true, "1337", "", "int", nil},
|
||||
{"b.C.s", true, "\"hello\"", "", "struct string", nil},
|
||||
{"b.s", true, "\"hello\"", "", "struct 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", nil},
|
||||
}
|
||||
assertNoError(p.Continue(), t, "Continue()")
|
||||
|
||||
@ -344,3 +347,170 @@ func TestComplexSetting(t *testing.T) {
|
||||
h("complex128(1.2, 3.4)", "(1.2 + 3.4i)")
|
||||
})
|
||||
}
|
||||
|
||||
func TestEvalExpression(t *testing.T) {
|
||||
testcases := []varTest{
|
||||
// slice/array/string subscript
|
||||
{"s1[0]", false, "\"one\"", "", "struct string", nil},
|
||||
{"s1[1]", false, "\"two\"", "", "struct string", nil},
|
||||
{"s1[2]", false, "\"three\"", "", "struct string", nil},
|
||||
{"s1[3]", false, "\"four\"", "", "struct string", nil},
|
||||
{"s1[4]", false, "\"five\"", "", "struct string", nil},
|
||||
{"s1[5]", false, "", "", "struct string", fmt.Errorf("index out of bounds")},
|
||||
{"a1[0]", false, "\"one\"", "", "struct string", nil},
|
||||
{"a1[1]", false, "\"two\"", "", "struct string", nil},
|
||||
{"a1[2]", false, "\"three\"", "", "struct string", nil},
|
||||
{"a1[3]", false, "\"four\"", "", "struct string", nil},
|
||||
{"a1[4]", false, "\"five\"", "", "struct string", nil},
|
||||
{"a1[5]", false, "", "", "struct string", fmt.Errorf("index out of bounds")},
|
||||
{"str1[0]", false, "48", "", "byte", nil},
|
||||
{"str1[1]", false, "49", "", "byte", nil},
|
||||
{"str1[2]", false, "50", "", "byte", nil},
|
||||
{"str1[10]", false, "48", "", "byte", nil},
|
||||
{"str1[11]", false, "", "", "byte", fmt.Errorf("index out of bounds")},
|
||||
|
||||
// slice/array/string reslicing
|
||||
{"a1[2:4]", false, "[]struct string len: 2, cap: 2, [\"three\",\"four\"]", "", "struct []struct string", nil},
|
||||
{"s1[2:4]", false, "[]string len: 2, cap: 2, [\"three\",\"four\"]", "", "struct []string", nil},
|
||||
{"str1[2:4]", false, "\"23\"", "", "struct string", nil},
|
||||
{"str1[0:11]", false, "\"01234567890\"", "", "struct string", nil},
|
||||
{"str1[:3]", false, "\"012\"", "", "struct string", nil},
|
||||
{"str1[3:]", false, "\"34567890\"", "", "struct string", nil},
|
||||
{"str1[0:12]", false, "", "", "struct string", fmt.Errorf("index out of bounds")},
|
||||
{"str1[5:3]", false, "", "", "struct string", fmt.Errorf("index out of bounds")},
|
||||
|
||||
// pointers
|
||||
{"*p2", false, "5", "", "int", nil},
|
||||
{"p2", true, "*5", "", "*int", nil},
|
||||
{"p3", true, "*int nil", "", "*int", nil},
|
||||
{"*p3", false, "", "", "int", fmt.Errorf("nil pointer dereference")},
|
||||
|
||||
// combined expressions
|
||||
{"c1.pb.a.A", true, "1", "", "int", nil},
|
||||
{"c1.sa[1].B", false, "3", "", "int", nil},
|
||||
{"s2[5].B", false, "12", "", "int", nil},
|
||||
{"s2[c1.sa[2].B].A", false, "11", "", "int", nil},
|
||||
{"s2[*p2].B", false, "12", "", "int", nil},
|
||||
|
||||
// constants
|
||||
{"1.1", false, "1.1", "", "", nil},
|
||||
{"10", false, "10", "", "", nil},
|
||||
{"1 + 2i", false, "(1 + 2i)", "", "", nil},
|
||||
{"true", false, "true", "", "", nil},
|
||||
{"\"test\"", false, "\"test\"", "", "", nil},
|
||||
|
||||
// binary operators
|
||||
{"i2 + i3", false, "5", "", "int", nil},
|
||||
{"i2 - i3", false, "-1", "", "int", nil},
|
||||
{"i3 - i2", false, "1", "", "int", nil},
|
||||
{"i2 * i3", false, "6", "", "int", nil},
|
||||
{"i2/i3", false, "0", "", "int", nil},
|
||||
{"f1/2.0", false, "1.5", "", "float64", nil},
|
||||
{"i2 << 2", false, "8", "", "int", nil},
|
||||
|
||||
// unary operators
|
||||
{"-i2", false, "-2", "", "int", nil},
|
||||
{"+i2", false, "2", "", "int", nil},
|
||||
{"^i2", false, "-3", "", "int", nil},
|
||||
|
||||
// comparison operators
|
||||
{"i2 == i3", false, "false", "", "", nil},
|
||||
{"i2 == 2", false, "true", "", "", nil},
|
||||
{"i2 == 2.0", false, "true", "", "", nil},
|
||||
{"i2 == 3", false, "false", "", "", nil},
|
||||
{"i2 != i3", false, "true", "", "", nil},
|
||||
{"i2 < i3", false, "true", "", "", nil},
|
||||
{"i2 <= i3", false, "true", "", "", nil},
|
||||
{"i2 > i3", false, "false", "", "", nil},
|
||||
{"i2 >= i3", false, "false", "", "", nil},
|
||||
{"i2 >= 2", false, "true", "", "", nil},
|
||||
{"str1 == \"01234567890\"", false, "true", "", "", nil},
|
||||
{"str1 < \"01234567890\"", false, "false", "", "", nil},
|
||||
{"str1 < \"11234567890\"", false, "true", "", "", nil},
|
||||
{"str1 > \"00234567890\"", false, "true", "", "", nil},
|
||||
{"str1 == str1", false, "true", "", "", nil},
|
||||
{"c1.pb.a == *(c1.sa[0])", false, "true", "", "", nil},
|
||||
{"c1.pb.a != *(c1.sa[0])", false, "false", "", "", nil},
|
||||
{"c1.pb.a == *(c1.sa[1])", false, "false", "", "", nil},
|
||||
{"c1.pb.a != *(c1.sa[1])", false, "true", "", "", nil},
|
||||
|
||||
// nil
|
||||
{"nil", false, "nil", "", "", nil},
|
||||
{"nil+1", false, "", "", "", fmt.Errorf("operator + can not be applied to \"nil\"")},
|
||||
{"fn1", false, "main.afunc", "", "main.functype", nil},
|
||||
{"fn2", false, "nil", "", "main.functype", nil},
|
||||
{"nilslice", false, "[]int len: 0, cap: 0, []", "", "struct []int", nil},
|
||||
{"fn1 == fn2", false, "", "", "", fmt.Errorf("can not compare func variables")},
|
||||
{"fn1 == nil", false, "false", "", "", nil},
|
||||
{"fn1 != nil", false, "true", "", "", nil},
|
||||
{"fn2 == nil", false, "true", "", "", nil},
|
||||
{"fn2 != nil", false, "false", "", "", nil},
|
||||
{"c1.sa == nil", false, "false", "", "", nil},
|
||||
{"c1.sa != nil", false, "true", "", "", nil},
|
||||
{"c1.sa[0] == nil", false, "false", "", "", nil},
|
||||
{"c1.sa[0] != nil", false, "true", "", "", nil},
|
||||
{"nilslice == nil", false, "true", "", "", nil},
|
||||
{"nilslice != nil", false, "false", "", "", nil},
|
||||
{"nilptr == nil", false, "true", "", "", nil},
|
||||
{"nilptr != nil", false, "false", "", "", nil},
|
||||
{"p1 == nil", false, "false", "", "", nil},
|
||||
{"p1 != nil", false, "true", "", "", nil},
|
||||
|
||||
// errors
|
||||
{"&3", false, "", "", "", fmt.Errorf("can not take address of \"3\"")},
|
||||
{"*3", false, "", "", "", fmt.Errorf("expression \"3\" can not be dereferenced")},
|
||||
{"&(i2 + i3)", false, "", "", "", fmt.Errorf("can not take address of \"(i2 + i3)\"")},
|
||||
{"i2 + p1", false, "", "", "", fmt.Errorf("mismatched types \"int\" and \"*int\"")},
|
||||
{"i2 + f1", false, "", "", "", fmt.Errorf("mismatched types \"int\" and \"float64\"")},
|
||||
{"i2 << f1", false, "", "", "", fmt.Errorf("shift count type float64, must be unsigned integer")},
|
||||
{"i2 << -1", false, "", "", "", fmt.Errorf("shift count type int, must be unsigned integer")},
|
||||
{"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")},
|
||||
}
|
||||
|
||||
withTestProcess("testvariables3", t, func(p *proc.Process, fixture protest.Fixture) {
|
||||
assertNoError(p.Continue(), t, "Continue() returned an error")
|
||||
for _, tc := range testcases {
|
||||
variable, err := evalVariable(p, tc.name)
|
||||
if tc.err == nil {
|
||||
assertNoError(err, t, fmt.Sprintf("EvalExpression(%s) returned an error", tc.name))
|
||||
assertVariable(t, variable, tc)
|
||||
} else {
|
||||
if err == nil {
|
||||
t.Fatalf("Expected error %s, got non (%s)", tc.err.Error(), tc.name)
|
||||
}
|
||||
if tc.err.Error() != err.Error() {
|
||||
t.Fatalf("Unexpected error. Expected %s got %s", tc.err.Error(), err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestEvalAddrAndCast(t *testing.T) {
|
||||
withTestProcess("testvariables3", t, func(p *proc.Process, fixture protest.Fixture) {
|
||||
assertNoError(p.Continue(), t, "Continue() returned an error")
|
||||
c1addr, err := evalVariable(p, "&c1")
|
||||
assertNoError(err, t, "EvalExpression(&c1)")
|
||||
c1addrstr := api.ConvertVar(c1addr).SinglelineString()
|
||||
t.Logf("&c1 → %s", c1addrstr)
|
||||
if !strings.HasPrefix(c1addrstr, "(*main.cstruct)(0x") {
|
||||
t.Fatalf("Invalid value of EvalExpression(&c1) \"%s\"", c1addrstr)
|
||||
}
|
||||
|
||||
aaddr, err := evalVariable(p, "&(c1.pb.a)")
|
||||
assertNoError(err, t, "EvalExpression(&(c1.pb.a))")
|
||||
aaddrstr := api.ConvertVar(aaddr).SinglelineString()
|
||||
t.Logf("&(c1.pb.a) → %s", aaddrstr)
|
||||
if !strings.HasPrefix(aaddrstr, "(*main.astruct)(0x") {
|
||||
t.Fatalf("invalid value of EvalExpression(&(c1.pb.a)) \"%s\"", aaddrstr)
|
||||
}
|
||||
|
||||
a, err := evalVariable(p, "*"+aaddrstr)
|
||||
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})
|
||||
})
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user