JDK-8247517 : Final fields in records are not reflectively modifiable
  • Type: CSR
  • Component: core-libs
  • Sub-Component: java.lang
  • Priority: P3
  • Status: Closed
  • Resolution: Approved
  • Fix Versions: 15
  • Submitted: 2020-06-13
  • Updated: 2020-06-20
  • Resolved: 2020-06-17
Related Reports
CSR :  
Description
Summary
-------

Make final fields in records not reflectively modifiable such that final fields in records are trusted for JIT optimization.

Problem
-------

Final fields are not trusted because they are not truly final and can be modified via reflection.   For new features, it's desirable to make final fields truly final where possible.

Solution
--------

Make final fields in records not modifiable via reflection.  A JIT compiler can trust these final fields truly final.

`Field::setAccessible(true)` will succeed to allow existing frameworks to have read access to fields.  If it's a final field in a record class, it's not modifiable.  `Field::set` will throw `IllegalAccessException` because it is not modifiable, i.e. no write access.

Specification
-------------

`java.lang.reflect.AccessibleObject::setAccessible` specifies that final fields in records are not modifiable.  As specified in `Field::set`, static final fields of any class or interface cannot be modified.  This spec change includes the static final fields in the list of non-modifiable fields for completeness. 

```
--- a/src/java.base/share/classes/java/lang/reflect/AccessibleObject.java
+++ b/src/java.base/share/classes/java/lang/reflect/AccessibleObject.java
@@ -177,10 +177,16 @@
      * to the caller's module. </p>
      *
      * <p> This method cannot be used to enable {@linkplain Field#set <em>write</em>}
-     * access to a final field declared in a {@linkplain Class#isHidden() hidden class},
-     * since such fields are not modifiable.  The {@code accessible} flag when
-     * {@code true} suppresses Java language access control checks to only
-     * enable {@linkplain Field#get <em>read</em>} access to such fields.
+     * access to a <em>non-modifiable</em> final field.  The following fields
+     * are non-modifiable:
+     * <ul>
+     * <li>static final fields declared in any class or interface</li>
+     * <li>final fields declared in a {@linkplain Class#isHidden() hidden class}</li>
+     * <li>final fields declared in a {@linkplain Class#isRecord() record}</li>
+     * </ul>
+     * <p> The {@code accessible} flag when {@code true} suppresses Java language access
+     * control checks to only enable {@linkplain Field#get <em>read</em>} access to
+     * these non-modifiable final fields.
      *
```

`java.lang.reflect.Field::set` is updated as follows:

```
      * <p>If the underlying field is final, this {@code Field} object has
      * <em>write</em> access if and only if the following conditions are met:
      * <ul>
      * <li>{@link #setAccessible(boolean) setAccessible(true)} has succeeded for
      *     this {@code Field} object;</li>
      * <li>the field is non-static; and</li>
      * <li>the field's declaring class is not a {@linkplain Class#isHidden()
-     *     hidden class}.</li>
+     *     hidden class}; and</li>
+     * <li>the field's declaring class is not a {@linkplain Class#isRecord()
+     *     record class}.</li>
```
Comments
Thanks [~mchung]; moving to Approved.
17-06-2020

[~darcy] The proposed spec change includes the static final fields declared in any class or interface is a spec clarification. It has been a long-standing behavior that static final fields cannot be modified that is specified in the javadoc of `Field::set`: ``` If the underlying field is final, this Field object has write access if and only if the following conditions are met: - setAccessible(true) has succeeded for this Field object; - the field is non-static; and - the field's declaring class is not a hidden class. If any of the above checks is not met, this method throws an IllegalAccessException. ``` I have clarified that in the CSR. I moved it back to Finalized state.
16-06-2020

[~dholmes] Thanks. I made this minor edit in this CSR and my local repo.
16-06-2020

+ * access to a <em>non-modifiable</em> final field. The following fields + * are not modifiable: The second line should read "are non-modifiable" as that is the term you are defining.
16-06-2020

Moving to Provisional, not Approved. [~mchung], the proposed spec update includes: * <li>static final fields declared in any class or interface</li> which seems a much larger change than just altering the behavior for records. Is this the intended scope of the change? Or is it already the cases that static final fields cannot be modified?
16-06-2020