Unexpected equality behavior (java.lang.Long)

Hannes Wallnöfer hannes.wallnoefer at oracle.com
Wed Aug 17 05:56:22 UTC 2016


It’s unfortunate JS considers null to be of type ‚object‘. I think that in most practical circumstances and definitely when a number comes from a Java primitive you can omit the null check as Java primitives can’t be null, though.

Hannes


> Am 16.08.2016 um 16:16 schrieb João Paulo Varandas <joaovarandas at inpaas.com>:
> 
> Hi Hannes.
> 
> Thanks for reviewing this. Seeing it from this perspective:
> 
> Longs are just like JS Number wrappers.
> 
> Made me believe the situation was not that bad too. Telling developers to 'unwrap' numbers using " x = Number(x); " would not be a bad idea.
> 
> With that in mind, any number comparison would have to be made unwrapping data first ... But, we would also need to test nulls first, because:
> 
> While "null == 0" yields false, and obviusly "null === 0" yields false.
> Number(null) === Number(0) would yield true.
> 
> Thus, we would need:
> 
> function unwrapNumber(x) {
>    return x == null ? null : Number(x);
> }
> 
> And also, to tell developers to call unwrapNumber() everytime they need to compare a Number ... 
> 
> I don't know if you agree with me, but now I see this as an anti-pattern.
> 
> 
> ---
> 
> Anyway... It seems that all the fuzz is because of that "bug" with Long numbers and double. And I'm sorry that I did not look further into that issue.
> 
> Maybe we should look further into that, and try to make that any number below Number.MAX_SAFE_INTEGER (9007199254740991), work the way is expected inside JS evaluations ... 
> 
> 
> 
> 
> 
> 
>  
>  
> 
> 	João Paulo Varandas
> +55 11 99889-2321
> joaovarandas at inpaas.com
> 
> 
> 2016-08-16 6:23 GMT-03:00 Hannes Wallnöfer <hannes.wallnoefer at oracle.com>:
> After exploring various options I don’t think there’s anything we can do to go back and treat longs like numbers in Nashorn.
> 
> The ECMA spec is quite clear that there are numbers and object, and comparison of the latter is by reference, not value. So having some kind of in-between states is not really an option. Making longs numbers again would be quite a small and harmless change within the language (without reverting to longs as internal data type). Precision would be lowered to doubles for large longs, but that wouldn’t be a problem for most users.
> 
> However, the problems starts where longs as numbers inherit from Number.prototype. Of course, all methods in Number.prototype are specified for double values, and the toString methods collides with toString in java.lang.Long. Having a toString method that rounds large longs to the nearest double value is a no-go. Nor is extending all methods in Number.prototype to handle longs are viable options.
> 
> There is one issue we need to fix to improve handling of longs and other instances of java.lang.Number though (thanks to Attila for first pointing this out): Currently, java.lang.Numbers not treated as numbers in the ToPrimitive operation with Number hint. This means less-than/greater-than operators will treat those number objects as strings instead of converting to double. I filed a bug for this, and will back port it to JDK 8u:
> 
> https://bugs.openjdk.java.net/browse/JDK-8163945
> 
> All things considered, there are some things that make me think the situation is not as bad as some seem to believe. For one, the new treatment of Longs (with the fix for the bug above) is consistent with how JavaScript Number objects/wrappers are treated. Comparing two Number wrappers for the same value will also evaluate to false with both == and === operators, while comparing them to the primitive number with == will evaluate to true.
> 
> new Number(1) == new Number(1) // false
> new Number(1) === new Number(1) // false
> new Number(1) == 1 // true
> new Number(1) === 1 // false
> 
> So Longs are just like JS Number wrappers. They are object wrappers for numeric values. If needed they can easily converted to primitive numbers by calling the global Number() function without „new“ keyword, eg:
> 
> if (typeof x === „object“)
>     x = Number(x);
> 
> This is a common pattern in JavaScript even without Java longs, so I think it’s acceptable.
> 
> Hannes
> 
> 
> > Am 05.08.2016 um 15:45 schrieb Hannes Wallnöfer <hannes.wallnoefer at oracle.com>:
> >
> > Hi Joao Paulo,
> >
> > thanks for the report.
> >
> > We do realize that this change caused problems for a lot of people, and we are sorry for that, and thinking how we can improve things.
> >
> > I still think we were right to remove longs as internal number representation. But maybe we went to far with this, and should allow people to deal with longs as numbers, assuming they are all right with an eventual loss of presentation.
> >
> > This needs some discussion and thinking and experimentation. I’ll get back to you after the weekend.
> >
> > Hannes
> >
> >
> >
> >> Am 05.08.2016 um 04:40 schrieb João Paulo Varandas <joaovarandas at inpaas.com>:
> >>
> >> By the way, I just found out some bugs filed about Long objects:
> >> https://bugs.openjdk.java.net/browse/JDK-8162771
> >> https://bugs.openjdk.java.net/browse/JDK-8161665
> >>
> >> And a twitter discussion:
> >> https://twitter.com/provegard/status/755010492112986112
> >>
> >> I'd like to bring this up again if we have more room for discussion ...
> >> --
> >>
> >> 2 Long values returned from a database and sent to a javascript evaluation code can't be compared the way it should in JavaScript...  If I have some validation code, I don't know if the source is some JavaScript input (number), or the source is a database query (object/Long/Integer/Double).
> >>
> >> It's really hard to decide when sometimes x==y should be compared as x.equals(y) and sometimes not... and sometimes, at the same point in code I should do a lot of conditional operations, to resolve a simple comparison... and that comparison does not work if the data source is a Java object or a JavaScript object.
> >>
> >> The problem with the current solution is that comparison between different types would never work ...
> >>
> >> Seeing that
> >> jjs> new java.lang.Long(2147483648) == "2147483648"
> >> returns true
> >>
> >> But
> >> jjs> new java.lang.Long(2147483648) == new java.lang.Long(2147483648)
> >> returns false
> >>
> >> Is something really hard to accept ...
> >> And that "equals" approach will work ONLY if I compare Long to Long. And I see this as a regression when talking about evaluating JavaScript code.
> >>
> >>
> >>
> >> Sorry about the long post, but I would really appreciate if we could revalidate this and think about what the current situation represents from ecmascript's point of view.
> >>
> >>
> >>
> >>
> >>
> >> -08-04 22:43 GMT-03:00 João Paulo Varandas <joaovarandas at inpaas.com>:
> >> Sorry Tomas.
> >>
> >> Either you did not read the message(which would be indeed a beginner's mistake), or you are misleading the discussion.
> >>
> >> What you said, about equality and references would be true if we were talking about Java code. This is jdk8-nashorn's mailing list and I'm talking about JavaScript evaluation inside the JVM.
> >>
> >>
> >> Comparison operators inside the script engine follow JavaScript rules and not Java rules.
> >>
> >>
> >>
> >>
> >>
> >>
> >>
> >>
> >>
> >>
> >>
> >>
> >>
> >>      João Paulo Varandas
> >> +55 11 99889-2321
> >> joaovarandas at inpaas.com
> >>
> >>
> >> 2016-08-04 20:08 GMT-03:00 Tomáš Zíma <tomas at tzima.cz>:
> >> A classical beginner's mistake. You are using Java objects and comparing references, not the values, so this is expected behavior. Simply compare the objects using equals().
> >>
> >>
> >> On 4.8.2016 15:48, João Paulo Varandas wrote:
> >> Hi guys!
> >>
> >> It seems that version 1.8.0_101 has a bug in equality for same java data
> >> types (java.lang.Long).
> >>
> >> Check that:
> >> jjs> new java.lang.Long(10) == new java.lang.Long(10)
> >> false
> >>
> >> Oops!?
> >>
> >> See the gist:
> >> https://gist.github.com/joaovarandas/51567bd3b576d48a4c574d60d5a60ba3
> >>
> >> The results for all types should be ...
> >> ==    true
> >> ===   true
> >> equals true
> >>
> >> But for java.lang.Long and java.math.BigDecimal:
> >> ==    false
> >> ===   false
> >> equals true
> >>
> >> Maybe we could expand the test to other classes too, but the issue ...
> >> - It does happen in 1.8.0_101.
> >> - It does not 1.8.0_91.
> >> - It does happen with other classes (BigDecimal).
> >> - It does not happen with String.
> >> - It does not happen with Integer.
> >>
> >>
> >> I understand this is not an expected behavior. For now I'm rolling back
> >> to 1.8.0_91 in my environments.
> >>
> >>
> >>
> >> Do you me to file a bug?
> >>
> >> Thanks
> >> J
> >>
> >>
> >>
> >>
> >>
> >>
> >> "Esta mensagem, incluindo seus anexos, pode conter informacoes confidenciais e privilegiadas.
> >> Se voce a recebeu por engano, solicitamos que a apague e avise o remetente imediatamente.
> >> Opinioes ou informacoes aqui contidas nao refletem necessariamente a posicao oficial da Plusoft."
> >>
> >> "Antes de imprimir, pense em sua responsabilidade e compromisso com o MEIO AMBIENTE"
> >
> 
> 
> 
> "Esta mensagem, incluindo seus anexos, pode conter informacoes confidenciais e privilegiadas. 
> Se voce a recebeu por engano, solicitamos que a apague e avise o remetente imediatamente. 
> Opinioes ou informacoes aqui contidas nao refletem necessariamente a posicao oficial da Plusoft."
> 
> "Antes de imprimir, pense em sua responsabilidade e compromisso com o MEIO AMBIENTE"
> 



More information about the nashorn-dev mailing list