For now, AutoBeans expects that the input matches the expected format (and value ranges). The behavior seems kind of "undefined" when that's not the case, and not consistent between the VM and translatable implementations: * null values in some cases: dates in Java * exceptions in others: calling asNumber on a JsonSplittable without checking for isNumber can throw a NullPointerException; and Long.parseLong can throw a NumberFormatException * or even NaN: dates in JS; calling asNumber on a JsoSplittable without checking for isNumber can return NaN * or coercion to boolean: asBoolean on a JsoSplittable (note that on a JsonSplittable it could instead throw a NullPointerException)
If you want this to be fixed (which would be a good thing), I'd suggest doing it in a subsequent CL addressing all of these: error cases and discrepancy between Java and JS implementations. http://gwt-code-reviews.appspot.com/1601805/diff/2004/user/src/com/google/web/bindery/autobean/shared/ValueCodex.java File user/src/com/google/web/bindery/autobean/shared/ValueCodex.java (right): http://gwt-code-reviews.appspot.com/1601805/diff/2004/user/src/com/google/web/bindery/autobean/shared/ValueCodex.java#newcode107 user/src/com/google/web/bindery/autobean/shared/ValueCodex.java:107: return new Date((long) value.asNumber()); On 2012/04/05 20:34:10, skybrian wrote:
Since asNumber() returns a double, not a long, I expect this will lose
precision
once we are above 2^53. (What's that as a date, anyway?)
That's theoretically right, except: * this is about decoding, so the loss of precision if any would be in Splittable, not ValueCodex. If that's a problem, then Splittable could have an added isLong/asLong (and then we'd wonder about BigInteger/BigDecimal ;-) ). If you look at JsonSplittable, it explicitly rounds values to doubles (calls doubleValue() on any Number) * this would only happen on a Java client, because a Number in JavaScript is equivalent to a double in Java; a JSON input with a numeric value outside JS Number range would raise an exception at parsing time, before we reach this code * a Date in JavaScript has an even smaller range than a Number. ECMAScript 5 says (§15.9.1.1, p. 174): """ECMAScript Number values can represent all integers from –9,007,199,254,740,991 to 9,007,199,254,740,991; this range suffices to measure times to millisecond precision for any instant that is within approximately 285,616 years, either forward or backward, from 01 January, 1970 UTC. """The actual range of times supported by ECMAScript Date objects is slightly smaller: exactly –100,000,000 days to 100,000,000 days measured relative to midnight at the beginning of 01 January, 1970 UTC. This gives a range of 8,640,000,000,000,000 milliseconds to either side of 01 January, 1970 UTC.""" If you ask me, I'd be OK to leave the behavior undefined for such "out of range" values, on the basis that no-one would use them anyway. The behavior is already kind of undefined in the case of a String, as it'd try to create a JsDate (when compiled in JS) from the String, which is implementation-dependent (ECMAScript 5 added a specific format inspired by ISO8601, but allows implementation to use a fallback: "If the String does not conform to that format the function may fall back to any implementation-specific heuristics or implementation-specific date formats."). Furthermore, in JS, neither the JsDate.create() nor the Date(long) emulation should throw, so in case of an "invalid" String value, you'd get a "NaN Date"; in Java though, StringQuoter.tryParseDate uses Long.parseLong, ISO8601 and RFC2822 formats and then falls back to a 'null' value (which, as I said, you'd never get in JS). It might be something to fix, but that's a distinct issue (if any). http://gwt-code-reviews.appspot.com/1601805/diff/2004/user/src/com/google/web/bindery/autobean/shared/ValueCodex.java#newcode170 user/src/com/google/web/bindery/autobean/shared/ValueCodex.java:170: return Long.valueOf((long) value.asNumber()); On 2012/04/05 20:34:10, skybrian wrote:
I expect this breaks once we get above 2^53?
Actually, it'll lose precision in Java (JsonSplittable would call doubleValue() on the Long parsed by org.json), and fail while parsing the JSON in JavaScript (Number cannot be represented). It's a shame that JSON (RFC 4627) doesn't define a clear and interoperable range for numbers, but I guess everyone using JSON sticks to using numeric values that can be parsed in JavaScript, i.e. falls in the java.lang.Double value space. http://gwt-code-reviews.appspot.com/1601805/diff/2004/user/test/com/google/web/bindery/autobean/benchmarks/ValueCodexBenchmark.java File user/test/com/google/web/bindery/autobean/benchmarks/ValueCodexBenchmark.java (right): http://gwt-code-reviews.appspot.com/1601805/diff/2004/user/test/com/google/web/bindery/autobean/benchmarks/ValueCodexBenchmark.java#newcode45 user/test/com/google/web/bindery/autobean/benchmarks/ValueCodexBenchmark.java:45: public void testEncode() { On 2012/04/05 20:34:10, skybrian wrote:
Empty test... not sure what's going on here.
AFAICT, these zero-arg methods are needed so that JUnit sees the tests, and then the BenchmarkShell will actually run the methods with annotated arguments. Will add a comment. http://gwt-code-reviews.appspot.com/1601805/diff/2004/user/test/com/google/web/bindery/autobean/shared/AutoBeanCodexTest.java File user/test/com/google/web/bindery/autobean/shared/AutoBeanCodexTest.java (right): http://gwt-code-reviews.appspot.com/1601805/diff/2004/user/test/com/google/web/bindery/autobean/shared/AutoBeanCodexTest.java#newcode237 user/test/com/google/web/bindery/autobean/shared/AutoBeanCodexTest.java:237: AutoBean<HasLong> decodedBean = AutoBeanCodex.decode(f, HasLong.class, "{\"long\": 42}"); On 2012/04/05 20:34:10, skybrian wrote:
Let's try a wider range. I expect it breaks for Long.MAX_VALUE and Long.MIN_VALUE. (And below.)
See comments in ValueCodex. I can add tests using "8_640_000_000_000_000L" (value space for JS dates), or (long)Double.MAX_VALUE and (long)Double.MIN_VALUE in testLong, that are expected to pass both in JS and a Java VM, but for values outside those ranges the behavior is currently "undefined" (or at least inconsistent between JS and Java). I think I simply copied from testSimple() which doesn't exercise "edge cases" (also notice that the whole class only exercises the "happy paths", should we add tests for expected failures? may I suggest we do it in a subsequent CL?) http://gwt-code-reviews.appspot.com/1601805/ -- http://groups.google.com/group/Google-Web-Toolkit-Contributors