I think there is a way to avoid all the issues we have with new Object().
The problem:
We want to inject the interface IdentityObject on all classes that are
neither a "no field" abstract class nor a primtive class.
But java.lang.Object acts as a "no field" abstract class but it's not an
abstract class.
Clearly, there are two java.lang.Object, the one which is the root of all
classes (the "no field" abstract one) and the one which can be instantiated
that should implements IdentityObject.
The logical conclusion is that java.lang.Object is a parametric class with a
parameter saying if it should implement IdentityObject or not.
The "raw" version does not implement IdentityObject, that the one used when a
class extends Object.
The parameterized version implements IdentityObject and is the one used by new
Object()
So the classfile Object.class should contain a SpecializationAnchor, and being
a "no field" class or not is an attribute that references the Anchor.
To maintain backward compatibility, when the VM sees the opcode NEW
java/lang/Object, it adds a new constant pool entry of kind
SpecializationLinkage that reference java/lang/Object with a constant asking
for version that implements IdentityObject and rewrite NEW java/lang/Object to
NEW Linkage(java/lang/Object).
Obviously, there is the bootstraping issue with the code in java.lang.invoke
making the BSM working not be initialized at the time java.lang.Object need to
be available, so the VM has to inject the binding early as if it was resolved
by the BSM but i think it's workable.
When the VM starts, if there are some new Object() executedbefore the code of
java.lang.invoke is ready, those code can be rewritten to use a special class
defined in the JDK for that like
package jdk.internal.whatever;
class LockObject { }
Even if it's not strictly necessary, javac can be changed to emit the
Linkage(java/lang/Object) each time a new Object() is needed to the rewriting
by the VM described above need to be done only for the classfiles that are
using previous versions.
I believe this change is fully source and binary compatible and nice property
is that
new Object().getClass() == Object.class
is still true.
Rémi