The 'Field.setAccessible' method, which provides a standard API mechanism for mutating final fields, considers strict finals to be "non-modifiable", and will not enable reflective writes. (It already does the same for record fields.)

We discussed this at the EG meeting and I'm still uncomfortable.

There are two not-completely-coincident sets of constraints:

 - Final instance fields that can be modified using setAccessible
 - Final instance fields that are trusted by the VM to stay constant

The former is specified as: final instance fields that are not in records and hidden classes, as well as the modularity constraint that prevent use of setAccessible in the absence of an "opens" edge (see AccessibleObject::checkCanSetAccessible).

The latter is a larger and somewhat more embarassing set: hidden classes, box classes, record classes, String, atomic field updaters, and a whitelisted set of packages (java.lang, java.lang.{invoke,reflect}, sun.invoke, jdk.internal.{reflect,foreign.layout,foreign,vm.vector}, jdk.incubator.vector (except for the weird in/out fields in System).  Plus a flag to just turn on "trust them all, except the weird ones in System".

The former is specified through JDK specifications; the latter is a VM implementation detail that conservatively gives up optimization when we cannot prove that initialization is race-free.

Adding "value classes" to both lists seems reasonable.  Driving towards fewer modifiable final fields is a desirable goal; driving towards all final fields being trustable is also.

But this proposal also adds another entry to both lists: "final fields whose initialization precedes the super-call in all constructors."  This is not an easily describable property of the programming model, and it is pretty hard to explain why this is relevant.  Their presence on the first list may show up as weird breakage with existing frameworks under harmless-looking refactorings.  Plus, since we don't have a reasonable description for these fields at the language level (unlike "fields in records, hidden classes, or value classes"), it's hard to even talk about.

The stated purpose of the setAccessible loophole is deserialization; the existence of the non-trusting of final fields is due to the possibility that the JIT might inline the wrong value, either due to races (if the field is set after this escapes) or setAccessible shenanigans.

On the other hand, I think we all agree that we don't want a `really-final-i-mean-it` modifier on fields, and that if we'd like for most classes to be strictly initialized, then having a strict modifier on classes/ctors is another "wrong default".

tl;dr: I think the "strict field" formulation beyond that needed for value classes needs some more bake time.

Reply via email to