提交 d89d115e 编写于 作者: A Alessandro Arzilli 提交者: Derek Parker

proc/variables: support NaN/Inf float values (#706)

Unfortunately go/constant does not support NaN and Inf float values so
we need to store this information alongside.

Fixes #705
上级 449b276f
......@@ -3,6 +3,7 @@ package main
import (
"fmt"
"go/constant"
"math"
"runtime"
"unsafe"
)
......@@ -207,6 +208,10 @@ func main() {
ni16 := int16(-5)
ni32 := int32(-5)
pinf := math.Inf(+1)
ninf := math.Inf(-1)
nan := math.NaN()
var amb1 = 1
runtime.Breakpoint()
for amb1 := 0; amb1 < 10; amb1++ {
......@@ -214,5 +219,5 @@ func main() {
}
runtime.Breakpoint()
fmt.Println(i1, i2, i3, p1, amb1, s1, s3, a1, p2, p3, s2, as1, str1, f1, fn1, fn2, nilslice, nilptr, ch1, chnil, m1, mnil, m2, m3, up1, i4, i5, i6, err1, err2, errnil, iface1, iface2, ifacenil, arr1, parr, cpx1, const1, iface3, iface4, recursive1, recursive1.x, iface5, iface2fn1, iface2fn2, bencharr, benchparr, mapinf, mainMenu, b, b2, sd, anonstruct1, anonstruct2, anoniface1, anonfunc, mapanonstruct1, ifacearr, efacearr, ni8, ni16, ni32)
fmt.Println(i1, i2, i3, p1, amb1, s1, s3, a1, p2, p3, s2, as1, str1, f1, fn1, fn2, nilslice, nilptr, ch1, chnil, m1, mnil, m2, m3, up1, i4, i5, i6, err1, err2, errnil, iface1, iface2, ifacenil, arr1, parr, cpx1, const1, iface3, iface4, recursive1, recursive1.x, iface5, iface2fn1, iface2fn2, bencharr, benchparr, mapinf, mainMenu, b, b2, sd, anonstruct1, anonstruct2, anoniface1, anonfunc, mapanonstruct1, ifacearr, efacearr, ni8, ni16, ni32, pinf, ninf, nan)
}
......@@ -3,6 +3,7 @@ package proc
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"go/ast"
"go/constant"
......@@ -15,6 +16,8 @@ import (
"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)
......@@ -688,6 +691,9 @@ func (scope *EvalScope) evalUnary(node *ast.UnaryExpr) (*Variable, error) {
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))
}
......@@ -794,6 +800,10 @@ func (scope *EvalScope) evalBinary(node *ast.BinaryExpr) (*Variable, error) {
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
......
......@@ -8,6 +8,7 @@ import (
"go/constant"
"go/parser"
"go/token"
"math"
"reflect"
"strings"
"unsafe"
......@@ -29,6 +30,15 @@ const (
hashMinTopHash = 4 // used by map reading code, indicates minimum value of tophash that isn't empty or evacuated
)
type FloatSpecial uint8
const (
FloatIsNormal FloatSpecial = iota
FloatIsNaN
FloatIsPosInf
FloatIsNegInf
)
// Variable represents a variable. It contains the address, name,
// type and other information parsed from both the Dwarf information
// and the memory of the debugged process.
......@@ -43,7 +53,8 @@ type Variable struct {
mem memoryReadWriter
dbp *Process
Value constant.Value
Value constant.Value
FloatSpecial FloatSpecial
Len int64
Cap int64
......@@ -808,6 +819,14 @@ func (v *Variable) loadValueInternal(recurseLevel int, cfg LoadConfig) {
var val float64
val, v.Unreadable = v.readFloatRaw(v.RealType.(*dwarf.FloatType).ByteSize)
v.Value = constant.MakeFloat64(val)
switch {
case math.IsInf(val, +1):
v.FloatSpecial = FloatIsPosInf
case math.IsInf(val, -1):
v.FloatSpecial = FloatIsNegInf
case math.IsNaN(val):
v.FloatSpecial = FloatIsNaN
}
case reflect.Func:
v.readFunctionPtr()
default:
......
......@@ -98,6 +98,19 @@ func prettyTypeName(typ dwarf.Type) string {
return r
}
func convertFloatValue(v *proc.Variable, sz int) string {
switch v.FloatSpecial {
case proc.FloatIsPosInf:
return "+Inf"
case proc.FloatIsNegInf:
return "-Inf"
case proc.FloatIsNaN:
return "NaN"
}
f, _ := constant.Float64Val(v.Value)
return strconv.FormatFloat(f, 'f', -1, sz)
}
// ConvertVar converts from proc.Variable to api.Variable.
func ConvertVar(v *proc.Variable) *Variable {
r := Variable{
......@@ -119,11 +132,9 @@ func ConvertVar(v *proc.Variable) *Variable {
if v.Value != nil {
switch v.Kind {
case reflect.Float32:
f, _ := constant.Float64Val(v.Value)
r.Value = strconv.FormatFloat(f, 'f', -1, 32)
r.Value = convertFloatValue(v, 32)
case reflect.Float64:
f, _ := constant.Float64Val(v.Value)
r.Value = strconv.FormatFloat(f, 'f', -1, 64)
r.Value = convertFloatValue(v, 64)
case reflect.String, reflect.Func:
r.Value = constant.StringVal(v.Value)
default:
......
......@@ -448,6 +448,11 @@ func TestEvalExpression(t *testing.T) {
{"str1[0:12]", false, "", "", "string", fmt.Errorf("index out of bounds")},
{"str1[5:3]", false, "", "", "string", fmt.Errorf("index out of bounds")},
// NaN and Inf floats
{"pinf", false, "+Inf", "+Inf", "float64", nil},
{"ninf", false, "-Inf", "-Inf", "float64", nil},
{"nan", false, "NaN", "NaN", "float64", nil},
// pointers
{"*p2", false, "5", "5", "int", nil},
{"p2", true, "*5", "(*int)(0x…", "*int", nil},
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册