diff --git a/v1/tasks/reflect.go b/v1/tasks/reflect.go index 53aa7eb7..a17a04b5 100644 --- a/v1/tasks/reflect.go +++ b/v1/tasks/reflect.go @@ -6,6 +6,7 @@ import ( "encoding/json" "fmt" "reflect" + "strconv" "strings" ) @@ -288,21 +289,21 @@ func getIntValue(theType string, value interface{}) (int64, error) { } func getUintValue(theType string, value interface{}) (uint64, error) { - // We use https://golang.org/pkg/encoding/json/#Decoder.UseNumber when unmarshaling signatures. - // This is because JSON only supports 64-bit floating point numbers and we could lose precision - // when converting from float64 to unsigned integer + // Losing precision only happens in receiving a JSON number from a language like js, + // and receiving a large uint number from golang or python could cause json.Number.Int64 be turned into a panic. + // So we use strconv.ParseUint to correctly parse a uint value. if strings.HasPrefix(fmt.Sprintf("%T", value), "json.Number") { n, ok := value.(json.Number) if !ok { return 0, typeConversionError(value, typesMap[theType].String()) } - intVal, err := n.Int64() + uintVal, err := strconv.ParseUint(string(n), 10, 64) if err != nil { return 0, err } - return uint64(intVal), nil + return uintVal, nil } var n uint64 diff --git a/v1/tasks/reflect_test.go b/v1/tasks/reflect_test.go index 8593666c..392af5e4 100644 --- a/v1/tasks/reflect_test.go +++ b/v1/tasks/reflect_test.go @@ -81,6 +81,12 @@ var ( expectedType: "uint64", expectedValue: uint64(185135722552891243), }, + { + name: "uint64", + value: json.Number("9223372036854775808"), // math.MaxInt64 + 1 + expectedType: "uint64", + expectedValue: uint64(9223372036854775808), + }, { name: "float32", value: json.Number("0.5"), diff --git a/v2/tasks/reflect.go b/v2/tasks/reflect.go index 53aa7eb7..a17a04b5 100644 --- a/v2/tasks/reflect.go +++ b/v2/tasks/reflect.go @@ -6,6 +6,7 @@ import ( "encoding/json" "fmt" "reflect" + "strconv" "strings" ) @@ -288,21 +289,21 @@ func getIntValue(theType string, value interface{}) (int64, error) { } func getUintValue(theType string, value interface{}) (uint64, error) { - // We use https://golang.org/pkg/encoding/json/#Decoder.UseNumber when unmarshaling signatures. - // This is because JSON only supports 64-bit floating point numbers and we could lose precision - // when converting from float64 to unsigned integer + // Losing precision only happens in receiving a JSON number from a language like js, + // and receiving a large uint number from golang or python could cause json.Number.Int64 be turned into a panic. + // So we use strconv.ParseUint to correctly parse a uint value. if strings.HasPrefix(fmt.Sprintf("%T", value), "json.Number") { n, ok := value.(json.Number) if !ok { return 0, typeConversionError(value, typesMap[theType].String()) } - intVal, err := n.Int64() + uintVal, err := strconv.ParseUint(string(n), 10, 64) if err != nil { return 0, err } - return uint64(intVal), nil + return uintVal, nil } var n uint64 diff --git a/v2/tasks/reflect_test.go b/v2/tasks/reflect_test.go index 3fe9e692..1892ca31 100644 --- a/v2/tasks/reflect_test.go +++ b/v2/tasks/reflect_test.go @@ -81,6 +81,12 @@ var ( expectedType: "uint64", expectedValue: uint64(185135722552891243), }, + { + name: "uint64", + value: json.Number("9223372036854775808"), // math.MaxInt64 + 1 + expectedType: "uint64", + expectedValue: uint64(9223372036854775808), + }, { name: "float32", value: json.Number("0.5"),