Field initialization before 'super'
Remi Forax
forax at univ-mlv.fr
Sat Jan 27 08:00:20 UTC 2024
----- Original Message -----
> From: "daniel smith" <daniel.smith at oracle.com>
> To: "amber-spec-experts" <amber-spec-experts at openjdk.java.net>
> Sent: Friday, January 26, 2024 10:48:18 PM
> Subject: Re: Field initialization before 'super'
> Having worked through the JLS changes (which I'll be sharing at some point
> soon), here are a few extra details that I think make the most sense:
>
>> On Dec 12, 2023, at 4:27 PM, Dan Smith <daniel.smith at oracle.com> wrote:
>>
>> To enable and take advantage of early field initialization, we've envisioned the
>> following changes:
>>
>> 1) As an exception to the general rule about 'this' usage, a "pre-construction
>> context" allows writes to blank instance fields of the class. (The terminology
>> may need updating, since you're clearly "constructing" the object if you're
>> writing to its fields.) The fields are "write-only" at this stage—you can write
>> into them but can't read them back.
>
> Terminology: maybe "early construction context" rather than "pre-construction
> context"?
>
> Enabling the capability to write to *blank* instance fields, but not fields with
> instance initializers, sidesteps any confusion about the timing of initializer
> execution, while still giving programmers the capability to prevent any
> unwanted reads of a field's default (null/zero) value, whether the field is
> final or not. (Would it be nice if fields with initializers could also, in some
> cases, be initialized early? Yes, but it's an incompatible change, so more work
> needed to navigate that problem.)
agree,
>
>> At a 'this()' call, all final fields must be DU (because the delegated
>> constructor will perform its own writes). No such restriction is needed for
>> non-final fields; but it's an open question whether we should prohibit all
>> writes before 'this()' anyway.
>
> In the interest of not making arbitrary language rules, I prefer not to
> special-case the early construction context of a 'this'-calling constructor,
> other than introducing the DU rule for final fields.
agree,
>
>> Writes to non-final fields with initializers are disallowed, to avoid confusion
>> about sequencing (the field initializer will always run later, overwriting
>> whatever you put in the constructor prologue.)
>
> Yeah, this was the main concern about assignments before 'this()' calls. If you
> can't write to fields with initializers, then the timing of writes to a mutable
> field should be clear from the code. Whether it's a good way to structure a
> program is a stylistic choice. <shrug>
>
> ---
>
> And some comments about other proposed features:
>
>> 2) If a final field is written before 'super()' via every constructor in the
>> class, it can be considered a "strict final" field. It will never be observed
>> to mutate.
>>
>> In the class file, ACC_STRICT is repurposed to indicate a strict final field.
>> javac is responsible for identifying strict final fields. Existing
>> early-initialized capture fields can probably be automatically counted as
>> strict finals.
>>
>> ACC_STRICT implies ACC_FINAL and !ACC_STATIC. Verification ensures that a
>> 'putfield' for an ACC_STRICT field of the current class never occurs after the
>> 'super()' call. (Specifically, the receiver type for the putfield must be
>> 'uninitializedThis', not a class type.)
agree
>
> Although there are limits to how much we can do with the flag just yet (see
> below), I think it probably makes sense to identify these fields in class files
> as ACC_STRICT as part of this JEP.
>
>> 3) Immutability of strict finals is a strong guarantee.
>
> This jumps the gun.
>
> ACC_STRICT is a claim about local code: that the field will not be mutated by
> the code of the class after the 'super()' call.
>
> A global claim about immutability relies on other integrity properties of the
> JDK as a whole. There's a path to getting those integrity properties, but it's
> beyond the scope of this JEP.
>
I disagree here.
As John said, the aim here is safe publication of 'this', under any conditions, i.e. even if 'this' leaked from the constructor.
Currently, we have safe publication if 'this' does not leak from the constructor, the idea is with strict final fields is that that it give you the guarantee of safe publication even if 'this' is leaked.
I think the JEP should be reworded so allowing codes before super() has two advantages, early checks before field assignments and safe publication of 'this'.
For the latter, the compiler should not compile
- if one final field is strict but other final fields are not
(we still allow all final fields to be non strict for backward compatibility)
- the super class final fields should be strict
(this one hamper migration of the subclasses without changing of the superclass but it's a necessary evil IMO to get safe publication guarantee)
>> JVM internals may treat strict final fields as truly immutable, without
>> supporting any deopt paths when unexpected mutation occurs.
>
> This will be true only after we can make a global claim about immutability
> (ACC_STRICT, so not mutated by the class itself; plus all off-label mutation
> paths have been blocked or disavowed.)
>
As said above, i prefer to think in terms of safe publication of this than in terms of immutability.
Using immutability here is not quite right, because it's only shallow immutability, unmodifiability is better term and this is not exactly unmodifiability because we also allow non final fields.
>> 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.)
>
> This felt too ad hoc.
>
> A better path is to follow in the footsteps of "Prepare to Restrict the Use of
> JNI" (https://openjdk.org/jeps/8307341), gradually limiting the use of
> 'setAccessible' for final field mutation (ACC_STRICT or not) to users who
> explicitly opt in. That would be a separate effort.
>
I think it's simpler than that.
Strict final field means there is no way see a field with another value than the value set before calling the super constructor.
So let's not add special way to escape that guarantee.
setAccessible() should not allow to modify a strict field.
What we can do later is to retrofit the fields of records and hidden classes to be strict (and also add name and ordinal of java.lang.Enum). So instead of having an hadoc list of when a final field is writable by reflection or not we only have one condition which is if the final field is strict. But this is a separate effort.
>> Standard deserialization ensures strict finals are set, and so their values
>> deserialized, before the object under construction is leaked to any user code.
>> This probably means back references to an object from its own strict final
>> fields are unsupported, and deserialize to 'null'. (Records already behave in
>> this way.)
>
> I think this would be nice to have, paired with ACC_STRICT, but we'll see
> whether we can get there or not. If not, it's an improvement that can come
> later.
>
Again here, I would like to avoid an askterisk saying we have safe publication of 'this' but not in case of serialization.
>> Unsafe and JNI are capable of performing arbitrary, type-unsafe modifications to
>> field storage. Clients who modify strict finals do so at their own risk, and
>> JVM optimizations won't try to account for such usage.
>
> I noted the attempts to restrict use of JNI above; but in any case, I think this
> statement is still fair: JVM internals do not need to account for misuse of
> Unsafe/JNI.
To set a field with Unsafe, one need the offset of that field, and the method to get a field offset in Unsafe should throw an exception the same way this is currently done with record fields.
With that, the compîler, reflection, unsafe and serialization should all conspire to maintain the garantee of safe publication of 'this'.
regards,
Rémi
More information about the amber-spec-experts
mailing list