mirror of
https://github.com/beego/bee.git
synced 2024-11-27 03:01:29 +00:00
1122 lines
29 KiB
Go
1122 lines
29 KiB
Go
package proc
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"fmt"
|
|
"go/ast"
|
|
"go/constant"
|
|
"go/parser"
|
|
"go/printer"
|
|
"go/token"
|
|
"reflect"
|
|
|
|
"github.com/derekparker/delve/dwarf/reader"
|
|
"golang.org/x/debug/dwarf"
|
|
)
|
|
|
|
// 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" {
|
|
return scope.Thread.getGVariable()
|
|
} 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.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 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.Thread.dbp.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.Thread.dbp, scope.Thread)
|
|
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.Thread), 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.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
|
|
}
|
|
|
|
// 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.Thread.dbp.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.Thread.dbp.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.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
|
|
}
|
|
|
|
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
|
|
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
|
|
}
|