Interface pollution and JVM invariants
Dan Heidinga
heidinga at redhat.com
Fri Jan 28 21:28:58 UTC 2022
I've spent a good chunk of my day trying to work out whether the
behaviour described below is problematic or expected consequences of
interface (heap?) pollution.
My conclusion is that it's expected behaviour due to the way
interfaces are handled by the verifier and may, in limited places, be
problematic enough to require an explicit check that an instance is or
is not a value. In most cases, it's business as usual.
I thought it worth putting this on record to ensure there are no other
concerns about it.
Longer writeup:
-----
Pulling together threads from a couple of recent EG mail discussions
to pose a concern I've been thinking about. This touches on the IO/VO
interfaces, the new proposed ctor for WeakReferences, and the job of
the verifier.
Let's start with WeakReference. One potential path forward there is
to have a new ctor that takes an IdentityObject rather than an Object.
Javac can do the right thing and force all the callers to only pass
IdentityObjects.
class WeakReference<T extends IdentityObject> {
T referent;
public WeakReference(T o) { referent = o; }
}
Assuming I have the generics right, that ctor will take an
IdentityObject in its descriptor. Everything's good from the language
type-system's point of view.
Along comes a classfile generator (or carefully crafted separate
compilation) that passes a ValueObject to that WeakReference ctor.
Since the Verifier has special handling of interfaces, this generated
class verifies successfully. And if no checkcast or interface send
occurs, nothing is required to validate that the referent is actually
an IdentityObject.
The source level invariant is lost at the bytecode level and we allow
the `referent` field to be polluted with a wrong kind of instance.
This can be patched by have the ctor check that the incoming object
isn't a value:
public WeakReference(T o) {
if (o.getClass().isValue()) throw IAE;
referent = o;
}
That kind of check is easy to miss (or assume isn't required) based on
a straightforward reading of the source code.
I like the IO/VO interfaces as they let us put the constraints "must
be identity (or not)" in the type system but having them as interfaces
means the guarantees aren't strictly enforced by the runtime.
In most cases, this won't be that problematic - an unexpected
exception at worst? - except for cases like WeakReference where there
are invariants from the rest of the JVM that need to be enforced.
So maybe not an issue.
--Dan
More information about the valhalla-spec-experts
mailing list