Closed kkarnauk closed 7 months ago
To solve the issue, we need to track down the classes that override hashCode. One unreliable but simple solution would be to check whether obj.hashCode() == System.identityHashCode(obj)
to detect that hashCode()
returns the identity hash code. Thanks to @kkarnauk for the suggestion. While this simple solution is imperfect, it is super-easy to implement and should work fine in practice.
The main question is "what System.identityHashCode(obj)
should return?". I suggest generating a pseudo-random number and associating it with the object for further calls.
Should be resolved in #249
Version: 2.16 (and the previous ones, too)
Issue
As you can see in the implementation of HashCodeStubTransformer, in order to understand whether it should change a call of
hashCode
, it checkesowner
. But it doesn't take into account thatowner
means the actual callee on the stack (not the concrete class which method will be called).So, there are following problems with that:
If we cast an object to
Any/Object
with overridenhashCode
and call it, then0
is returned. For example:I suppose, it is a huge hole. First of all, it becomes inconistent and the
equals-hashCode
invariant doesn't work. Also, in most collections all objects are casted toAny/Object
, so allhashCode
invocations return0
and collections start working slow.If we have a class that doesn't override
hashCode
(and so do its parents) andowner != Object
, then no transformation is done. It still calls the native implementation (which usually returns the memory address) and we have the non-determinism.How to fix
I have the following proposal. There are two fixes (one for each problem):
owner
isAny/Object
and if it's true, we check::class
of the value on the stack. If it is actuallyObject/Any
, then we need to do the transformation.Object
directly and doesn't override the defaulthashCode()
, it should add an implementation ofhashCode
which returns 0.