This task summarizes the JVM changes introduced by the Strict Field Initialization in the JVM JEP. All features are activated by the `--enable-preview` launcher option.
Full JVMS changes to come...
### ACC_STRICT flag
Any field may be marked `ACC_STRICT` (`0x0800`, used in legacy classes to indicate `strictfp` semantics for methods, but no longer meaningful) to indicate that it is strictly initialized. The flag may be used in combination with any other flag.
### Instance initialization states
The initialization state of the current class instance is tracked by the verifier using one of the following:
- *earlyLarval(unsetFields)*, where *unsetFields* is a list of strict instance fields of the current class that need to be set
- *unrestricted*, a general-purpose symbol covering the *late larval* and *initialized* states, and also used for static methods
- *erroneous*, indicating that initialization has failed, and preventing any exception handlers (a placeholder symbol similar, in some ways, to *afterGoto*)
These initialization state values replace the 'flags' verification parameter. (Note that, with the exception of the list of unset fields, this is just a presentational change, expressing the combination of 'flags' and an available 'uninitializedThis'.)
The initial state of an <init> method is *earlyLarval(...)*, with unset fields including all strict instance fields declared by the class. The initial state of any other method is *unrestricted*.
The 'frameIsAssignable' predicate only allows transitions between identical states, or between different *earlyLarval* states where the targeted frame has a superset of unset fields.
### Verification of instructions
'putfield':
- In an *earlyLarval(...)* state, when operating on 'uninitializedThis' and referencing the current class, the outgoing initialization state removes the named field, if present, from the list of uninitialized fields (the exceptional state is unchanged)
- For other 'putfield' instructions, reject an attempt to write to a 'strict' 'final' instance field referenced & declared in the current class
'invokespecial':
- When invoking an <init> method on an uninitializedThis, the incoming initialization state must be *earlyLarval(...)*. The outgoing initialization state is *unrestricted* and the exceptional initialization state is *erroneous*.
- When invoking an <init> method on an uninitializedThis, if the method is referenced in the superclass, the list of unset fields must be empty.
'return':
- The 'return' instruction is only allowed in the *unrestricted* state.
### StackMapTable changes
The StackMapTable is enhanced to allow the following entry, which is not a *frame*, but affects the interpretation of subsequent frames:
assert_early_unset_fields {
u1 frame_type = ASSERT_EARLY_UNSET_FIELDS; /* 246 */
u2 number_of_unset_fields;
u2 unset_fields[number_of_unset_fields];
}
Each unset_fields entry must reference a NameAndType constant with a field descriptor, and (probably?) that NameAndType must match a strict instance field declared in the current class, or the StackMapTable is malformed.
The stack frames of the StackMapTable are interpreted as follows:
- If the frame has a local variable of type 'uninitializedThis', the initialization state is *earlyLarval(...)*, with unset fields given by the closest preceding 'assert_early_unset_fields' entry (or, if none, the initial set of unset fields)
- If the frame does not have a local variable of type 'uninitializedThis', the initialization state is *unrestricted*
(Note that the StackMapTable does not support expressing a handler for the *erroneous* state of a failed invokespecial. This is intentional, consistent with current behavior.)
### Class initialization states
The *larval* class initialization state (referred to in JVMS as "being initialized") currently keeps track of the thread performing initialization; this JEP adds information to track whether each static field has been initialized, and whether each static final field has been read.
When the ConstantValue attribute of a field is applied, the class initialization state is updated to reflect that the field has been initialized.
### Static field checks
'putstatic' (and similar reflective operations), when the resolved field's declaring class is in a *larval* state:
- If the field is strict & static & final, the state must indicate that the field has not yet been read, or an exception (TBD) thrown
- If the field is static & final, the state must indicate that the field has not yet been read, or a diagnostic is generated (see below)
- If the field is static, the state is updated to indicate that the field has been initialized
'getstatic' (and similar reflective operations), when the resolved field's declaring class is in a *larval* state::
- If the field is strict & static, the state must indicate that the field has been initialized, or an exception (TBD) is thrown
- If the field is static, the state must indicate that the field has been initialized, or a diagnostic is generated (see below)
- If the field is static & final, the state is updated to indicate that the field has been read
After a '<clinit>' method is invoked (or, if none is declared, after it *would have* been invoked), the class initialization state must indicate that every strict static field of the class has been initialized, or an exception (TBD) is thrown.
### Static field diagnostics
A command-line flag (TBD) controls how diagnostics for non-strict fields generated during static field reads and writes are presented to users:
- By default, the event is ignored
- Under one configuration, the diagnostic is logged to the console and reported to JFR
- Under another configuration, the diagnostic results in a fatal error
Full JVMS changes to come...
### ACC_STRICT flag
Any field may be marked `ACC_STRICT` (`0x0800`, used in legacy classes to indicate `strictfp` semantics for methods, but no longer meaningful) to indicate that it is strictly initialized. The flag may be used in combination with any other flag.
### Instance initialization states
The initialization state of the current class instance is tracked by the verifier using one of the following:
- *earlyLarval(unsetFields)*, where *unsetFields* is a list of strict instance fields of the current class that need to be set
- *unrestricted*, a general-purpose symbol covering the *late larval* and *initialized* states, and also used for static methods
- *erroneous*, indicating that initialization has failed, and preventing any exception handlers (a placeholder symbol similar, in some ways, to *afterGoto*)
These initialization state values replace the 'flags' verification parameter. (Note that, with the exception of the list of unset fields, this is just a presentational change, expressing the combination of 'flags' and an available 'uninitializedThis'.)
The initial state of an <init> method is *earlyLarval(...)*, with unset fields including all strict instance fields declared by the class. The initial state of any other method is *unrestricted*.
The 'frameIsAssignable' predicate only allows transitions between identical states, or between different *earlyLarval* states where the targeted frame has a superset of unset fields.
### Verification of instructions
'putfield':
- In an *earlyLarval(...)* state, when operating on 'uninitializedThis' and referencing the current class, the outgoing initialization state removes the named field, if present, from the list of uninitialized fields (the exceptional state is unchanged)
- For other 'putfield' instructions, reject an attempt to write to a 'strict' 'final' instance field referenced & declared in the current class
'invokespecial':
- When invoking an <init> method on an uninitializedThis, the incoming initialization state must be *earlyLarval(...)*. The outgoing initialization state is *unrestricted* and the exceptional initialization state is *erroneous*.
- When invoking an <init> method on an uninitializedThis, if the method is referenced in the superclass, the list of unset fields must be empty.
'return':
- The 'return' instruction is only allowed in the *unrestricted* state.
### StackMapTable changes
The StackMapTable is enhanced to allow the following entry, which is not a *frame*, but affects the interpretation of subsequent frames:
assert_early_unset_fields {
u1 frame_type = ASSERT_EARLY_UNSET_FIELDS; /* 246 */
u2 number_of_unset_fields;
u2 unset_fields[number_of_unset_fields];
}
Each unset_fields entry must reference a NameAndType constant with a field descriptor, and (probably?) that NameAndType must match a strict instance field declared in the current class, or the StackMapTable is malformed.
The stack frames of the StackMapTable are interpreted as follows:
- If the frame has a local variable of type 'uninitializedThis', the initialization state is *earlyLarval(...)*, with unset fields given by the closest preceding 'assert_early_unset_fields' entry (or, if none, the initial set of unset fields)
- If the frame does not have a local variable of type 'uninitializedThis', the initialization state is *unrestricted*
(Note that the StackMapTable does not support expressing a handler for the *erroneous* state of a failed invokespecial. This is intentional, consistent with current behavior.)
### Class initialization states
The *larval* class initialization state (referred to in JVMS as "being initialized") currently keeps track of the thread performing initialization; this JEP adds information to track whether each static field has been initialized, and whether each static final field has been read.
When the ConstantValue attribute of a field is applied, the class initialization state is updated to reflect that the field has been initialized.
### Static field checks
'putstatic' (and similar reflective operations), when the resolved field's declaring class is in a *larval* state:
- If the field is strict & static & final, the state must indicate that the field has not yet been read, or an exception (TBD) thrown
- If the field is static & final, the state must indicate that the field has not yet been read, or a diagnostic is generated (see below)
- If the field is static, the state is updated to indicate that the field has been initialized
'getstatic' (and similar reflective operations), when the resolved field's declaring class is in a *larval* state::
- If the field is strict & static, the state must indicate that the field has been initialized, or an exception (TBD) is thrown
- If the field is static, the state must indicate that the field has been initialized, or a diagnostic is generated (see below)
- If the field is static & final, the state is updated to indicate that the field has been read
After a '<clinit>' method is invoked (or, if none is declared, after it *would have* been invoked), the class initialization state must indicate that every strict static field of the class has been initialized, or an exception (TBD) is thrown.
### Static field diagnostics
A command-line flag (TBD) controls how diagnostics for non-strict fields generated during static field reads and writes are presented to users:
- By default, the event is ignored
- Under one configuration, the diagnostic is logged to the console and reported to JFR
- Under another configuration, the diagnostic results in a fatal error
- relates to
-
JDK-8317278 JVM implementation of value classes and objects
-
- In Progress
-