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:
aarzilli 2015-10-30 12:39:32 +01:00
parent 6f4401654c
commit 43b64ec39e
10 changed files with 1299 additions and 326 deletions

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

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