Conversation
| return null; | ||
| } | ||
| return doubleInput; | ||
| } else if (isNumberIsh(input)) { |
There was a problem hiding this comment.
I did not add a separate else if (input instanceof Float) branch because it causes some tests to fail.
Getting the doubleValue() from a BigDecimal produces an ever-so-slightly-different result to getting the doubleValue() out of a Float.
There was a problem hiding this comment.
Current this is
private Double convertImpl(Object input) {
// From the GraphQL Float spec, non-finite floating-point internal values (NaN and Infinity)
// must raise a field error on both result and input coercion
if (input instanceof Double) {
Double doubleInput = (Double) input;
if (Double.isNaN(doubleInput) || Double.isInfinite(doubleInput)) {
return null;
}
return doubleInput;
} else if (isNumberIsh(input)) {
BigDecimal value;
try {
value = new BigDecimal(input.toString());
} catch (NumberFormatException e) {
return null;
}
return value.doubleValue();
} else {
return null;
}
}
So it does not apply the checks to the BigDecimal derived string
Why not do
private Double convertImpl(Object input) {
// From the GraphQL Float spec, non-finite floating-point internal values (NaN and Infinity)
// must raise a field error on both result and input coercion
Double doubleInput;
if (input instanceof Double) {
doubleInput = (Double) input;
} else if (isNumberIsh(input)) {
BigDecimal value;
try {
value = new BigDecimal(input.toString());
} catch (NumberFormatException e) {
return null;
}
doubleInput = value.doubleValue();
} else {
return null;
}
if (Double.isNaN(doubleInput) || Double.isInfinite(doubleInput)) {
return null;
}
return doubleInput
}
Forgive me if this doesnt compile
There was a problem hiding this comment.
This seems more sensible - eg first get it into a double and then check it - regardless
Even if BigDecimal never produces Nan or infinity, the logic reads better
There was a problem hiding this comment.
Oh no, it can produce infinity! This was a great idea
|
|
||
| public static final GraphQLObjectType queryType = newObject() | ||
| .name("QueryType") | ||
| // Static scalars |
There was a problem hiding this comment.
This was added a long time ago, before the GraphQL Float spec was updated to specifically not allow NaN and positive/negative infinity values
bbakerman
left a comment
There was a problem hiding this comment.
Can we tweak it as suggested or justify why not :)
| return null; | ||
| } | ||
| return doubleInput; | ||
| } else if (isNumberIsh(input)) { |
There was a problem hiding this comment.
Current this is
private Double convertImpl(Object input) {
// From the GraphQL Float spec, non-finite floating-point internal values (NaN and Infinity)
// must raise a field error on both result and input coercion
if (input instanceof Double) {
Double doubleInput = (Double) input;
if (Double.isNaN(doubleInput) || Double.isInfinite(doubleInput)) {
return null;
}
return doubleInput;
} else if (isNumberIsh(input)) {
BigDecimal value;
try {
value = new BigDecimal(input.toString());
} catch (NumberFormatException e) {
return null;
}
return value.doubleValue();
} else {
return null;
}
}
So it does not apply the checks to the BigDecimal derived string
Why not do
private Double convertImpl(Object input) {
// From the GraphQL Float spec, non-finite floating-point internal values (NaN and Infinity)
// must raise a field error on both result and input coercion
Double doubleInput;
if (input instanceof Double) {
doubleInput = (Double) input;
} else if (isNumberIsh(input)) {
BigDecimal value;
try {
value = new BigDecimal(input.toString());
} catch (NumberFormatException e) {
return null;
}
doubleInput = value.doubleValue();
} else {
return null;
}
if (Double.isNaN(doubleInput) || Double.isInfinite(doubleInput)) {
return null;
}
return doubleInput
}
Forgive me if this doesnt compile
| return null; | ||
| } | ||
| return doubleInput; | ||
| } else if (isNumberIsh(input)) { |
There was a problem hiding this comment.
This seems more sensible - eg first get it into a double and then check it - regardless
Even if BigDecimal never produces Nan or infinity, the logic reads better
| Double.NEGATIVE_INFINITY | _ | ||
| Float.NaN | _ | ||
| Float.POSITIVE_INFINITY | _ | ||
| Float.NEGATIVE_INFINITY | _ |
There was a problem hiding this comment.
Can we have a string rep here? Float.NEGATIVE_INFINITY.toString() ?
There was a problem hiding this comment.
public static void main(String[] args) {
String negInf = String.valueOf(Float.NEGATIVE_INFINITY);
System.out.println(negInf);
double dv = new BigDecimal(negInf).doubleValue();
System.out.println(dv);
}
-Infinity
Exception in thread "main" java.lang.NumberFormatException
at java.math.BigDecimal.<init>(BigDecimal.java:494)
at java.math.BigDecimal.<init>(BigDecimal.java:383)
at java.math.BigDecimal.<init>(BigDecimal.java:806)
So its not acceptable but lets test anyway to show that we handle it
| final Object serialized = serializeLegacy(type, value, graphqlContext, locale); | ||
| if (isNullishLegacy(serialized)) { | ||
| return null; | ||
| } |
There was a problem hiding this comment.
This NaN check was unnecessary. The NaN check already happened inside the serialize method.
| if (Double.isNaN(doubleInput) || Double.isInfinite(doubleInput)) { | ||
| return null; | ||
| } | ||
| return doubleInput; |
There was a problem hiding this comment.
Move NaN/Infinity check lower down to make this easier to read
There was a problem hiding this comment.
Edit: not only easier to read, it catches the edge case where value.doubleValue() returns positive/negative infinity
| Float.POSITIVE_INFINITY | _ | ||
| Float.POSITIVE_INFINITY.toString() | _ | ||
| Float.NEGATIVE_INFINITY | _ | ||
| Float.NEGATIVE_INFINITY.toString() | _ |
There was a problem hiding this comment.
Add String representations to test
| @@ -1,6 +1,13 @@ | |||
| package graphql.language | |||
| package graphql.execution | |||
There was a problem hiding this comment.
Tidy up package name and constructor usage
The GraphQL Float spec was updated a year ago to specifically not allow NaN and positive/negative infinity in both result coercion and input coercion
https://spec.graphql.org/draft/#sec-Float
graphql/graphql-spec#778
The float coercing was mostly compliant with this spec update, as NaN and positive/negative infinity are not allowed in the BigDecimal String constructor used in
GraphqlFloatCoercing, specifically the linevalue = new BigDecimal(input.toString());.I say mostly, there was one uncaught case. It's possible for the old
convertImplmethod to return a Double value that is later converted to positive infinity withBigDecimal.valueOf, such as in thevalueToLiteralImplThis PR introduces an explicit check for positive/negative infinity and NaN to address this problem. This PR cleans up NaN and infinity tests.