mirror of
				https://github.com/go-delve/delve.git
				synced 2025-11-04 06:32:16 +08:00 
			
		
		
		
	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
This commit is contained in:
		
				
					committed by
					
						
						Derek Parker
					
				
			
			
				
	
			
			
			
						parent
						
							449b276fe1
						
					
				
				
					commit
					d89d115ef9
				
			@ -3,6 +3,7 @@ package main
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"go/constant"
 | 
						"go/constant"
 | 
				
			||||||
 | 
						"math"
 | 
				
			||||||
	"runtime"
 | 
						"runtime"
 | 
				
			||||||
	"unsafe"
 | 
						"unsafe"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@ -207,6 +208,10 @@ func main() {
 | 
				
			|||||||
	ni16 := int16(-5)
 | 
						ni16 := int16(-5)
 | 
				
			||||||
	ni32 := int32(-5)
 | 
						ni32 := int32(-5)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pinf := math.Inf(+1)
 | 
				
			||||||
 | 
						ninf := math.Inf(-1)
 | 
				
			||||||
 | 
						nan := math.NaN()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var amb1 = 1
 | 
						var amb1 = 1
 | 
				
			||||||
	runtime.Breakpoint()
 | 
						runtime.Breakpoint()
 | 
				
			||||||
	for amb1 := 0; amb1 < 10; amb1++ {
 | 
						for amb1 := 0; amb1 < 10; amb1++ {
 | 
				
			||||||
@ -214,5 +219,5 @@ func main() {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	runtime.Breakpoint()
 | 
						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)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										10
									
								
								proc/eval.go
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								proc/eval.go
									
									
									
									
									
								
							@ -3,6 +3,7 @@ package proc
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"bytes"
 | 
						"bytes"
 | 
				
			||||||
	"encoding/binary"
 | 
						"encoding/binary"
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"go/ast"
 | 
						"go/ast"
 | 
				
			||||||
	"go/constant"
 | 
						"go/constant"
 | 
				
			||||||
@ -15,6 +16,8 @@ import (
 | 
				
			|||||||
	"golang.org/x/debug/dwarf"
 | 
						"golang.org/x/debug/dwarf"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var OperationOnSpecialFloatError = errors.New("operations on non-finite floats not implemented")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// EvalExpression returns the value of the given expression.
 | 
					// EvalExpression returns the value of the given expression.
 | 
				
			||||||
func (scope *EvalScope) EvalExpression(expr string, cfg LoadConfig) (*Variable, error) {
 | 
					func (scope *EvalScope) EvalExpression(expr string, cfg LoadConfig) (*Variable, error) {
 | 
				
			||||||
	t, err := parser.ParseExpr(expr)
 | 
						t, err := parser.ParseExpr(expr)
 | 
				
			||||||
@ -688,6 +691,9 @@ func (scope *EvalScope) evalUnary(node *ast.UnaryExpr) (*Variable, error) {
 | 
				
			|||||||
	if xv.Unreadable != nil {
 | 
						if xv.Unreadable != nil {
 | 
				
			||||||
		return nil, xv.Unreadable
 | 
							return nil, xv.Unreadable
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						if xv.FloatSpecial != 0 {
 | 
				
			||||||
 | 
							return nil, OperationOnSpecialFloatError
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	if xv.Value == nil {
 | 
						if xv.Value == nil {
 | 
				
			||||||
		return nil, fmt.Errorf("operator %s can not be applied to \"%s\"", node.Op.String(), exprToString(node.X))
 | 
							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
 | 
							return nil, yv.Unreadable
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if xv.FloatSpecial != 0 || yv.FloatSpecial != 0 {
 | 
				
			||||||
 | 
							return nil, OperationOnSpecialFloatError
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	typ, err := negotiateType(node.Op, xv, yv)
 | 
						typ, err := negotiateType(node.Op, xv, yv)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
 | 
				
			|||||||
@ -8,6 +8,7 @@ import (
 | 
				
			|||||||
	"go/constant"
 | 
						"go/constant"
 | 
				
			||||||
	"go/parser"
 | 
						"go/parser"
 | 
				
			||||||
	"go/token"
 | 
						"go/token"
 | 
				
			||||||
 | 
						"math"
 | 
				
			||||||
	"reflect"
 | 
						"reflect"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"unsafe"
 | 
						"unsafe"
 | 
				
			||||||
@ -29,6 +30,15 @@ const (
 | 
				
			|||||||
	hashMinTopHash   = 4 // used by map reading code, indicates minimum value of tophash that isn't empty or evacuated
 | 
						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,
 | 
					// Variable represents a variable. It contains the address, name,
 | 
				
			||||||
// type and other information parsed from both the Dwarf information
 | 
					// type and other information parsed from both the Dwarf information
 | 
				
			||||||
// and the memory of the debugged process.
 | 
					// and the memory of the debugged process.
 | 
				
			||||||
@ -43,7 +53,8 @@ type Variable struct {
 | 
				
			|||||||
	mem       memoryReadWriter
 | 
						mem       memoryReadWriter
 | 
				
			||||||
	dbp       *Process
 | 
						dbp       *Process
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	Value constant.Value
 | 
						Value        constant.Value
 | 
				
			||||||
 | 
						FloatSpecial FloatSpecial
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	Len int64
 | 
						Len int64
 | 
				
			||||||
	Cap int64
 | 
						Cap int64
 | 
				
			||||||
@ -808,6 +819,14 @@ func (v *Variable) loadValueInternal(recurseLevel int, cfg LoadConfig) {
 | 
				
			|||||||
		var val float64
 | 
							var val float64
 | 
				
			||||||
		val, v.Unreadable = v.readFloatRaw(v.RealType.(*dwarf.FloatType).ByteSize)
 | 
							val, v.Unreadable = v.readFloatRaw(v.RealType.(*dwarf.FloatType).ByteSize)
 | 
				
			||||||
		v.Value = constant.MakeFloat64(val)
 | 
							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:
 | 
						case reflect.Func:
 | 
				
			||||||
		v.readFunctionPtr()
 | 
							v.readFunctionPtr()
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
 | 
				
			|||||||
@ -98,6 +98,19 @@ func prettyTypeName(typ dwarf.Type) string {
 | 
				
			|||||||
	return r
 | 
						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.
 | 
					// ConvertVar converts from proc.Variable to api.Variable.
 | 
				
			||||||
func ConvertVar(v *proc.Variable) *Variable {
 | 
					func ConvertVar(v *proc.Variable) *Variable {
 | 
				
			||||||
	r := Variable{
 | 
						r := Variable{
 | 
				
			||||||
@ -119,11 +132,9 @@ func ConvertVar(v *proc.Variable) *Variable {
 | 
				
			|||||||
	if v.Value != nil {
 | 
						if v.Value != nil {
 | 
				
			||||||
		switch v.Kind {
 | 
							switch v.Kind {
 | 
				
			||||||
		case reflect.Float32:
 | 
							case reflect.Float32:
 | 
				
			||||||
			f, _ := constant.Float64Val(v.Value)
 | 
								r.Value = convertFloatValue(v, 32)
 | 
				
			||||||
			r.Value = strconv.FormatFloat(f, 'f', -1, 32)
 | 
					 | 
				
			||||||
		case reflect.Float64:
 | 
							case reflect.Float64:
 | 
				
			||||||
			f, _ := constant.Float64Val(v.Value)
 | 
								r.Value = convertFloatValue(v, 64)
 | 
				
			||||||
			r.Value = strconv.FormatFloat(f, 'f', -1, 64)
 | 
					 | 
				
			||||||
		case reflect.String, reflect.Func:
 | 
							case reflect.String, reflect.Func:
 | 
				
			||||||
			r.Value = constant.StringVal(v.Value)
 | 
								r.Value = constant.StringVal(v.Value)
 | 
				
			||||||
		default:
 | 
							default:
 | 
				
			||||||
 | 
				
			|||||||
@ -448,6 +448,11 @@ func TestEvalExpression(t *testing.T) {
 | 
				
			|||||||
		{"str1[0:12]", false, "", "", "string", fmt.Errorf("index out of bounds")},
 | 
							{"str1[0:12]", false, "", "", "string", fmt.Errorf("index out of bounds")},
 | 
				
			||||||
		{"str1[5:3]", 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
 | 
							// pointers
 | 
				
			||||||
		{"*p2", false, "5", "5", "int", nil},
 | 
							{"*p2", false, "5", "5", "int", nil},
 | 
				
			||||||
		{"p2", true, "*5", "(*int)(0x…", "*int", nil},
 | 
							{"p2", true, "*5", "(*int)(0x…", "*int", nil},
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user