
Before this commit our temp breakpoints only checked that we would stay on the same goroutine. However this isn't enough for recursive functions we must check that we stay on the same goroutine AND on the same stack frame (or, in the case of the StepOut breakpoint, the previous stack frame). This commit: 1. adds a new synthetic variable runtime.frameoff that returns the offset of the current frame from the base of the call stack. This is similar to runtime.curg 2. Changes the condition used for breakpoints on the lines of the current function to check that runtime.frameoff hasn't changed. 3. Changes the condition used for breakpoints on the return address to check that runtime.frameoff corresponds to the previous frame in the stack. 4. All other temporary breakpoints (the step-into breakpoints and defer breakpoints) remain unchanged. Fixes #828
1140 lines
30 KiB
Go
1140 lines
30 KiB
Go
package proc
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"errors"
|
|
"fmt"
|
|
"go/ast"
|
|
"go/constant"
|
|
"go/parser"
|
|
"go/printer"
|
|
"go/token"
|
|
"reflect"
|
|
|
|
"github.com/derekparker/delve/pkg/dwarf/reader"
|
|
"golang.org/x/debug/dwarf"
|
|
)
|
|
|
|
var OperationOnSpecialFloatError = errors.New("operations on non-finite floats not implemented")
|
|
|
|
// EvalExpression returns the value of the given expression.
|
|
func (scope *EvalScope) EvalExpression(expr string, cfg LoadConfig) (*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(cfg)
|
|
if ev.Name == "" {
|
|
ev.Name = expr
|
|
}
|
|
return ev, nil
|
|
}
|
|
|
|
func (scope *EvalScope) evalAST(t ast.Expr) (*Variable, error) {
|
|
switch node := t.(type) {
|
|
case *ast.CallExpr:
|
|
if len(node.Args) == 1 {
|
|
v, err := scope.evalTypeCast(node)
|
|
if err == nil {
|
|
return v, nil
|
|
}
|
|
_, isident := node.Fun.(*ast.Ident)
|
|
// we don't support function calls at the moment except for a few
|
|
// builtin functions so just return the type error here if the function
|
|
// isn't an identifier.
|
|
// More sophisticated logic will be required when function calls
|
|
// are implemented.
|
|
if err != reader.TypeNotFoundErr || !isident {
|
|
return v, err
|
|
}
|
|
}
|
|
return scope.evalBuiltinCall(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 maybePkg.Name == "runtime" && node.Sel.Name == "curg" {
|
|
if scope.Gvar == nil {
|
|
return nilVariable, nil
|
|
}
|
|
return scope.Gvar.clone(), nil
|
|
} else if maybePkg.Name == "runtime" && node.Sel.Name == "frameoff" {
|
|
return newConstant(constant.MakeInt64(scope.CFA-int64(scope.StackHi)), scope.Mem), nil
|
|
} else 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.TypeAssertExpr: // <expression>.(<type>)
|
|
return scope.evalTypeAssert(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.Mem), 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 type cast expressions
|
|
func (scope *EvalScope) evalTypeCast(node *ast.CallExpr) (*Variable, error) {
|
|
argv, err := scope.evalAST(node.Args[0])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
argv.loadValue(loadSingleValue)
|
|
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
|
|
}
|
|
|
|
styp, err := scope.BinInfo.findTypeExpr(fnnode)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
typ := resolveTypedef(styp)
|
|
|
|
converr := fmt.Errorf("can not convert %q to %s", exprToString(node.Args[0]), typ.String())
|
|
|
|
v := newVariable("", 0, styp, scope.BinInfo, scope.Mem)
|
|
v.loaded = true
|
|
|
|
switch ttyp := typ.(type) {
|
|
case *dwarf.PtrType:
|
|
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, converr
|
|
}
|
|
|
|
n, _ := constant.Int64Val(argv.Value)
|
|
|
|
v.Children = []Variable{*(scope.newVariable("", uintptr(n), ttyp.Type))}
|
|
return v, nil
|
|
|
|
case *dwarf.UintType:
|
|
switch argv.Kind {
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
n, _ := constant.Int64Val(argv.Value)
|
|
v.Value = constant.MakeUint64(convertInt(uint64(n), false, ttyp.Size()))
|
|
return v, nil
|
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
|
n, _ := constant.Uint64Val(argv.Value)
|
|
v.Value = constant.MakeUint64(convertInt(n, false, ttyp.Size()))
|
|
return v, nil
|
|
case reflect.Float32, reflect.Float64:
|
|
x, _ := constant.Float64Val(argv.Value)
|
|
v.Value = constant.MakeUint64(uint64(x))
|
|
return v, nil
|
|
}
|
|
case *dwarf.IntType:
|
|
switch argv.Kind {
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
n, _ := constant.Int64Val(argv.Value)
|
|
v.Value = constant.MakeInt64(int64(convertInt(uint64(n), true, ttyp.Size())))
|
|
return v, nil
|
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
|
n, _ := constant.Uint64Val(argv.Value)
|
|
v.Value = constant.MakeInt64(int64(convertInt(n, true, ttyp.Size())))
|
|
return v, nil
|
|
case reflect.Float32, reflect.Float64:
|
|
x, _ := constant.Float64Val(argv.Value)
|
|
v.Value = constant.MakeInt64(int64(x))
|
|
return v, nil
|
|
}
|
|
case *dwarf.FloatType:
|
|
switch argv.Kind {
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
fallthrough
|
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
|
fallthrough
|
|
case reflect.Float32, reflect.Float64:
|
|
v.Value = argv.Value
|
|
return v, nil
|
|
}
|
|
case *dwarf.ComplexType:
|
|
switch argv.Kind {
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
fallthrough
|
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
|
fallthrough
|
|
case reflect.Float32, reflect.Float64:
|
|
v.Value = argv.Value
|
|
return v, nil
|
|
}
|
|
}
|
|
|
|
return nil, converr
|
|
}
|
|
|
|
func convertInt(n uint64, signed bool, size int64) uint64 {
|
|
buf := make([]byte, 64/8)
|
|
binary.BigEndian.PutUint64(buf, n)
|
|
m := 64/8 - int(size)
|
|
s := byte(0)
|
|
if signed && (buf[m]&0x80 > 0) {
|
|
s = 0xff
|
|
}
|
|
for i := 0; i < m; i++ {
|
|
buf[i] = s
|
|
}
|
|
return uint64(binary.BigEndian.Uint64(buf))
|
|
}
|
|
|
|
func (scope *EvalScope) evalBuiltinCall(node *ast.CallExpr) (*Variable, error) {
|
|
fnnode, ok := node.Fun.(*ast.Ident)
|
|
if !ok {
|
|
return nil, fmt.Errorf("function calls are not supported")
|
|
}
|
|
|
|
args := make([]*Variable, len(node.Args))
|
|
|
|
for i := range node.Args {
|
|
v, err := scope.evalAST(node.Args[i])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
args[i] = v
|
|
}
|
|
|
|
switch fnnode.Name {
|
|
case "cap":
|
|
return capBuiltin(args, node.Args)
|
|
case "len":
|
|
return lenBuiltin(args, node.Args)
|
|
case "complex":
|
|
return complexBuiltin(args, node.Args)
|
|
case "imag":
|
|
return imagBuiltin(args, node.Args)
|
|
case "real":
|
|
return realBuiltin(args, node.Args)
|
|
}
|
|
|
|
return nil, fmt.Errorf("function calls are not supported")
|
|
}
|
|
|
|
func capBuiltin(args []*Variable, nodeargs []ast.Expr) (*Variable, error) {
|
|
if len(args) != 1 {
|
|
return nil, fmt.Errorf("wrong number of arguments to cap: %d", len(args))
|
|
}
|
|
|
|
arg := args[0]
|
|
invalidArgErr := fmt.Errorf("invalid argument %s (type %s) for cap", exprToString(nodeargs[0]), arg.TypeString())
|
|
|
|
switch arg.Kind {
|
|
case reflect.Ptr:
|
|
arg = arg.maybeDereference()
|
|
if arg.Kind != reflect.Array {
|
|
return nil, invalidArgErr
|
|
}
|
|
fallthrough
|
|
case reflect.Array:
|
|
return newConstant(constant.MakeInt64(arg.Len), arg.mem), nil
|
|
case reflect.Slice:
|
|
return newConstant(constant.MakeInt64(arg.Cap), arg.mem), nil
|
|
case reflect.Chan:
|
|
arg.loadValue(loadFullValue)
|
|
if arg.Unreadable != nil {
|
|
return nil, arg.Unreadable
|
|
}
|
|
if arg.Base == 0 {
|
|
return newConstant(constant.MakeInt64(0), arg.mem), nil
|
|
}
|
|
return newConstant(arg.Children[1].Value, arg.mem), nil
|
|
default:
|
|
return nil, invalidArgErr
|
|
}
|
|
}
|
|
|
|
func lenBuiltin(args []*Variable, nodeargs []ast.Expr) (*Variable, error) {
|
|
if len(args) != 1 {
|
|
return nil, fmt.Errorf("wrong number of arguments to len: %d", len(args))
|
|
}
|
|
arg := args[0]
|
|
invalidArgErr := fmt.Errorf("invalid argument %s (type %s) for len", exprToString(nodeargs[0]), arg.TypeString())
|
|
|
|
switch arg.Kind {
|
|
case reflect.Ptr:
|
|
arg = arg.maybeDereference()
|
|
if arg.Kind != reflect.Array {
|
|
return nil, invalidArgErr
|
|
}
|
|
fallthrough
|
|
case reflect.Array, reflect.Slice, reflect.String:
|
|
if arg.Unreadable != nil {
|
|
return nil, arg.Unreadable
|
|
}
|
|
return newConstant(constant.MakeInt64(arg.Len), arg.mem), nil
|
|
case reflect.Chan:
|
|
arg.loadValue(loadFullValue)
|
|
if arg.Unreadable != nil {
|
|
return nil, arg.Unreadable
|
|
}
|
|
if arg.Base == 0 {
|
|
return newConstant(constant.MakeInt64(0), arg.mem), nil
|
|
}
|
|
return newConstant(arg.Children[0].Value, arg.mem), nil
|
|
case reflect.Map:
|
|
it := arg.mapIterator()
|
|
if arg.Unreadable != nil {
|
|
return nil, arg.Unreadable
|
|
}
|
|
if it == nil {
|
|
return newConstant(constant.MakeInt64(0), arg.mem), nil
|
|
}
|
|
return newConstant(constant.MakeInt64(arg.Len), arg.mem), nil
|
|
default:
|
|
return nil, invalidArgErr
|
|
}
|
|
}
|
|
|
|
func complexBuiltin(args []*Variable, nodeargs []ast.Expr) (*Variable, error) {
|
|
if len(args) != 2 {
|
|
return nil, fmt.Errorf("wrong number of arguments to complex: %d", len(args))
|
|
}
|
|
|
|
realev := args[0]
|
|
imagev := args[1]
|
|
|
|
realev.loadValue(loadSingleValue)
|
|
imagev.loadValue(loadSingleValue)
|
|
|
|
if realev.Unreadable != nil {
|
|
return nil, realev.Unreadable
|
|
}
|
|
|
|
if imagev.Unreadable != nil {
|
|
return nil, imagev.Unreadable
|
|
}
|
|
|
|
if realev.Value == nil || ((realev.Value.Kind() != constant.Int) && (realev.Value.Kind() != constant.Float)) {
|
|
return nil, fmt.Errorf("invalid argument 1 %s (type %s) to complex", exprToString(nodeargs[0]), realev.TypeString())
|
|
}
|
|
|
|
if imagev.Value == nil || ((imagev.Value.Kind() != constant.Int) && (imagev.Value.Kind() != constant.Float)) {
|
|
return nil, fmt.Errorf("invalid argument 2 %s (type %s) to complex", exprToString(nodeargs[1]), imagev.TypeString())
|
|
}
|
|
|
|
sz := int64(0)
|
|
if realev.RealType != nil {
|
|
sz = realev.RealType.(*dwarf.FloatType).Size()
|
|
}
|
|
if imagev.RealType != nil {
|
|
isz := imagev.RealType.(*dwarf.FloatType).Size()
|
|
if isz > sz {
|
|
sz = isz
|
|
}
|
|
}
|
|
|
|
if sz == 0 {
|
|
sz = 128
|
|
}
|
|
|
|
typ := &dwarf.ComplexType{BasicType: dwarf.BasicType{CommonType: dwarf.CommonType{ByteSize: int64(sz / 8), Name: fmt.Sprintf("complex%d", sz)}, BitSize: sz, BitOffset: 0}}
|
|
|
|
r := realev.newVariable("", 0, typ)
|
|
r.Value = constant.BinaryOp(realev.Value, token.ADD, constant.MakeImag(imagev.Value))
|
|
return r, nil
|
|
}
|
|
|
|
func imagBuiltin(args []*Variable, nodeargs []ast.Expr) (*Variable, error) {
|
|
if len(args) != 1 {
|
|
return nil, fmt.Errorf("wrong number of arguments to imag: %d", len(args))
|
|
}
|
|
|
|
arg := args[0]
|
|
arg.loadValue(loadSingleValue)
|
|
|
|
if arg.Unreadable != nil {
|
|
return nil, arg.Unreadable
|
|
}
|
|
|
|
if arg.Kind != reflect.Complex64 && arg.Kind != reflect.Complex128 {
|
|
return nil, fmt.Errorf("invalid argument %s (type %s) to imag", exprToString(nodeargs[0]), arg.TypeString())
|
|
}
|
|
|
|
return newConstant(constant.Imag(arg.Value), arg.mem), nil
|
|
}
|
|
|
|
func realBuiltin(args []*Variable, nodeargs []ast.Expr) (*Variable, error) {
|
|
if len(args) != 1 {
|
|
return nil, fmt.Errorf("wrong number of arguments to real: %d", len(args))
|
|
}
|
|
|
|
arg := args[0]
|
|
arg.loadValue(loadSingleValue)
|
|
|
|
if arg.Unreadable != nil {
|
|
return nil, arg.Unreadable
|
|
}
|
|
|
|
if arg.Value == nil || ((arg.Value.Kind() != constant.Int) && (arg.Value.Kind() != constant.Float) && (arg.Value.Kind() != constant.Complex)) {
|
|
return nil, fmt.Errorf("invalid argument %s (type %s) to real", exprToString(nodeargs[0]), arg.TypeString())
|
|
}
|
|
|
|
return newConstant(constant.Real(arg.Value), arg.mem), 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.Mem), nil
|
|
case "nil":
|
|
return nilVariable, nil
|
|
}
|
|
|
|
// try to interpret this as a local variable
|
|
v, err := scope.extractVarInfo(node.Name)
|
|
if err == nil {
|
|
return v, nil
|
|
}
|
|
origErr := err
|
|
// workaround: sometimes go inserts an entry for '&varname' instead of varname
|
|
v, err = scope.extractVarInfo("&" + node.Name)
|
|
if err == nil {
|
|
v = v.maybeDereference()
|
|
v.Name = node.Name
|
|
return v, nil
|
|
}
|
|
// if it's not a local variable then it could be a package variable w/o explicit package name
|
|
_, _, fn := scope.BinInfo.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
|
|
}
|
|
|
|
// 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>.(<type>)
|
|
func (scope *EvalScope) evalTypeAssert(node *ast.TypeAssertExpr) (*Variable, error) {
|
|
xv, err := scope.evalAST(node.X)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if xv.Kind != reflect.Interface {
|
|
return nil, fmt.Errorf("expression \"%s\" not an interface", exprToString(node.X))
|
|
}
|
|
xv.loadInterface(0, false, loadFullValue)
|
|
if xv.Unreadable != nil {
|
|
return nil, xv.Unreadable
|
|
}
|
|
if xv.Children[0].Unreadable != nil {
|
|
return nil, xv.Children[0].Unreadable
|
|
}
|
|
if xv.Children[0].Addr == 0 {
|
|
return nil, fmt.Errorf("interface conversion: %s is nil, not %s", xv.DwarfType.String(), exprToString(node.Type))
|
|
}
|
|
typ, err := scope.BinInfo.findTypeExpr(node.Type)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if xv.Children[0].DwarfType.Common().Name != typ.Common().Name {
|
|
return nil, fmt.Errorf("interface conversion: %s is %s, not %s", xv.DwarfType.Common().Name, xv.Children[0].TypeString(), typ.Common().Name)
|
|
}
|
|
return &xv.Children[0], nil
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
idxev, err := scope.evalAST(node.Index)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
switch xev.Kind {
|
|
case reflect.Slice, reflect.Array, reflect.String:
|
|
if xev.Base == 0 {
|
|
return nil, fmt.Errorf("can not index \"%s\"", exprToString(node.X))
|
|
}
|
|
n, err := idxev.asInt()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return xev.sliceAccess(int(n))
|
|
|
|
case reflect.Map:
|
|
idxev.loadValue(loadFullValue)
|
|
if idxev.Unreadable != nil {
|
|
return nil, idxev.Unreadable
|
|
}
|
|
return xev.mapAccess(idxev)
|
|
default:
|
|
return nil, fmt.Errorf("expression \"%s\" (%s) does not support indexing", exprToString(node.X), xev.TypeString())
|
|
|
|
}
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
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)
|
|
}
|
|
}
|
|
|
|
switch xev.Kind {
|
|
case reflect.Slice, reflect.Array, reflect.String:
|
|
if xev.Base == 0 {
|
|
return nil, fmt.Errorf("can not slice \"%s\"", exprToString(node.X))
|
|
}
|
|
return xev.reslice(low, high)
|
|
case reflect.Map:
|
|
if node.High != nil {
|
|
return nil, fmt.Errorf("second slice argument must be empty for maps")
|
|
}
|
|
xev.mapSkip += int(low)
|
|
xev.loadValue(loadFullValue)
|
|
if xev.Unreadable != nil {
|
|
return nil, xev.Unreadable
|
|
}
|
|
return xev, nil
|
|
default:
|
|
return nil, fmt.Errorf("can not slice \"%s\" (type %s)", exprToString(node.X), xev.TypeString())
|
|
}
|
|
}
|
|
|
|
// 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.Kind != reflect.Ptr {
|
|
return nil, fmt.Errorf("expression \"%s\" (%s) can not be dereferenced", exprToString(node.X), xev.TypeString())
|
|
}
|
|
|
|
if xev == nilVariable {
|
|
return nil, fmt.Errorf("nil can not be dereferenced")
|
|
}
|
|
|
|
if len(xev.Children) == 1 {
|
|
// this branch is here to support pointers constructed with typecasts from ints
|
|
return &(xev.Children[0]), nil
|
|
}
|
|
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 || xev.DwarfType == nil {
|
|
return nil, fmt.Errorf("can not take address of \"%s\"", exprToString(node.X))
|
|
}
|
|
|
|
xev.OnlyAddr = true
|
|
|
|
typename := "*" + xev.DwarfType.Common().Name
|
|
rv := scope.newVariable("", 0, &dwarf.PtrType{CommonType: dwarf.CommonType{ByteSize: int64(scope.BinInfo.Arch.PtrSize()), Name: typename}, Type: xev.DwarfType})
|
|
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(loadSingleValue)
|
|
if xv.Unreadable != nil {
|
|
return nil, xv.Unreadable
|
|
}
|
|
if xv.FloatSpecial != 0 {
|
|
return nil, OperationOnSpecialFloatError
|
|
}
|
|
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 := xv.newVariable("", 0, xv.DwarfType)
|
|
r.Value = rc
|
|
return r, nil
|
|
}
|
|
return newConstant(rc, xv.mem), nil
|
|
}
|
|
|
|
func negotiateType(op token.Token, xv, yv *Variable) (dwarf.Type, error) {
|
|
if xv == nilVariable {
|
|
return nil, negotiateTypeNil(op, yv)
|
|
}
|
|
|
|
if yv == nilVariable {
|
|
return nil, negotiateTypeNil(op, xv)
|
|
}
|
|
|
|
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 negotiateTypeNil(op token.Token, v *Variable) error {
|
|
if op != token.EQL && op != token.NEQ {
|
|
return fmt.Errorf("operator %s can not be applied to \"nil\"", op.String())
|
|
}
|
|
switch v.Kind {
|
|
case reflect.Ptr, reflect.UnsafePointer, reflect.Chan, reflect.Map, reflect.Interface, reflect.Slice, reflect.Func:
|
|
return nil
|
|
default:
|
|
return fmt.Errorf("can not compare %s to nil", v.Kind.String())
|
|
}
|
|
}
|
|
|
|
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(loadFullValue)
|
|
yv.loadValue(loadFullValue)
|
|
|
|
if xv.Unreadable != nil {
|
|
return nil, xv.Unreadable
|
|
}
|
|
|
|
if yv.Unreadable != nil {
|
|
return nil, yv.Unreadable
|
|
}
|
|
|
|
if xv.FloatSpecial != 0 || yv.FloatSpecial != 0 {
|
|
return nil, OperationOnSpecialFloatError
|
|
}
|
|
|
|
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.mem), 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.mem), nil
|
|
}
|
|
|
|
r := xv.newVariable("", 0, typ)
|
|
r.Value = rc
|
|
if r.Kind == reflect.String {
|
|
r.Len = xv.Len + yv.Len
|
|
}
|
|
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
|
|
|
|
if xv == nilVariable {
|
|
switch op {
|
|
case token.EQL:
|
|
return yv.isNil(), nil
|
|
case token.NEQ:
|
|
return !yv.isNil(), nil
|
|
}
|
|
}
|
|
|
|
if yv == nilVariable {
|
|
switch op {
|
|
case token.EQL:
|
|
return xv.isNil(), nil
|
|
case token.NEQ:
|
|
return !xv.isNil(), nil
|
|
}
|
|
}
|
|
|
|
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, reflect.Chan:
|
|
return false, fmt.Errorf("can not compare %s variables", xv.Kind.String())
|
|
case reflect.Interface:
|
|
if xv.Children[0].RealType.String() != yv.Children[0].RealType.String() {
|
|
eql = false
|
|
} else {
|
|
eql, err = compareOp(token.EQL, &xv.Children[0], &yv.Children[0])
|
|
}
|
|
default:
|
|
return false, fmt.Errorf("unimplemented comparison of %s variables", xv.Kind.String())
|
|
}
|
|
|
|
if op == token.NEQ {
|
|
return !eql, err
|
|
}
|
|
return eql, err
|
|
}
|
|
|
|
func (v *Variable) isNil() bool {
|
|
switch v.Kind {
|
|
case reflect.Ptr:
|
|
return v.Children[0].Addr == 0
|
|
case reflect.Interface:
|
|
return false
|
|
case reflect.Slice, reflect.Map, reflect.Func, reflect.Chan:
|
|
return v.Base == 0
|
|
}
|
|
return false
|
|
}
|
|
|
|
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 (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(loadSingleValue)
|
|
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) asUint() (uint64, error) {
|
|
if v.DwarfType == nil {
|
|
if v.Value.Kind() != constant.Int {
|
|
return 0, fmt.Errorf("can not convert constant %s to uint", v.Value)
|
|
}
|
|
} else {
|
|
v.loadValue(loadSingleValue)
|
|
if v.Unreadable != nil {
|
|
return 0, v.Unreadable
|
|
}
|
|
if _, ok := v.DwarfType.(*dwarf.UintType); !ok {
|
|
return 0, fmt.Errorf("can not convert value of type %s to uint", v.DwarfType.String())
|
|
}
|
|
}
|
|
n, _ := constant.Uint64Val(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 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.StringType:
|
|
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 v.newVariable("", v.Base+uintptr(int64(idx)*v.stride), v.fieldType), nil
|
|
}
|
|
|
|
func (v *Variable) mapAccess(idx *Variable) (*Variable, error) {
|
|
it := v.mapIterator()
|
|
if it == nil {
|
|
return nil, fmt.Errorf("can not access unreadable map: %v", v.Unreadable)
|
|
}
|
|
|
|
first := true
|
|
for it.next() {
|
|
key := it.key()
|
|
key.loadValue(loadFullValue)
|
|
if key.Unreadable != nil {
|
|
return nil, fmt.Errorf("can not access unreadable map: %v", key.Unreadable)
|
|
}
|
|
if first {
|
|
first = false
|
|
if err := idx.isType(key.DwarfType, key.Kind); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
eql, err := compareOp(token.EQL, key, idx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if eql {
|
|
return it.value(), nil
|
|
}
|
|
}
|
|
if v.Unreadable != nil {
|
|
return nil, v.Unreadable
|
|
}
|
|
// go would return zero for the map value type here, we do not have the ability to create zeroes
|
|
return nil, fmt.Errorf("key not found")
|
|
}
|
|
|
|
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.SliceType{
|
|
StructType: dwarf.StructType{
|
|
CommonType: dwarf.CommonType{
|
|
ByteSize: 24,
|
|
Name: "",
|
|
},
|
|
StructName: fmt.Sprintf("[]%s", v.fieldType.Common().Name),
|
|
Kind: "struct",
|
|
Field: nil,
|
|
},
|
|
ElemType: v.fieldType,
|
|
}
|
|
}
|
|
|
|
r := v.newVariable("", 0, typ)
|
|
r.Cap = len
|
|
r.Len = len
|
|
r.Base = base
|
|
r.stride = v.stride
|
|
r.fieldType = v.fieldType
|
|
|
|
return r, nil
|
|
}
|