JDK-8253263 : Make hash code in DecimalFormatSymbols not serializeable
  • Type: CSR
  • Component: core-libs
  • Sub-Component: java.text
  • Priority: P4
  • Status: Closed
  • Resolution: Approved
  • Fix Versions: 16
  • Submitted: 2020-09-16
  • Updated: 2020-09-22
  • Resolved: 2020-09-22
Related Reports
CSR :  
Description
Summary
-------

Make the cached hash code in `java.text.DecimalFormatSymbols` not serializable by adding `transient` keyword to its field declaration.

Problem
-------

Since JDK15, `java.text.DecimalFormatSymbols` started caching the hash code which is lazily initialized at the first `hashCode()` invocation. As the field is declared as non-transient, the value is serialized and reused when it is read in `readObject()`. Although it won't cause any issue if serialize/deserialize is being done in JDK15 as the value calculation is fixed, the code should not rely on that implementation detail, as hash codes are not required to remain consistent beyond JVM invocations.

Solution
--------

Add `transient` keyword to the cached hash code field so that it won't be serialized. The field will always be zero-reset on each object construction so that the value is calculated on its first invocation of `hashCode()` method.

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

Change the cached hash code field declaration as follows:

    -- a/src/java.base/share/classes/java/text/DecimalFormatSymbols.java
    +++ b/src/java.base/share/classes/java/text/DecimalFormatSymbols.java
    @@ -760,7 +760,6 @@
         /**
          * Override hashCode.
          */
    -    private volatile int hashCode;
         @Override
         public int hashCode() {
             if (hashCode == 0) {
    @@ -1148,6 +1147,11 @@
         private transient Currency currency;
         private transient volatile boolean currencyInitialized;
     
    +    /**
    +     * Cached hash code.
    +     */
    +    private transient volatile int hashCode;
    +
         // Proclaim JDK 1.1 FCS compatibility
         @java.io.Serial
         static final long serialVersionUID = 5772796243397350300L;


Comments
Thanks, Joe. That is right.
22-09-2020

So to check my understanding of the case analysis, there are three version of the class of interest with respect to this change: 1. Pre-JDK 15 without the hashCode field 2. JDK 15 with the hashCode field 3. JDK 16 without the hashCode field (assuming this change goes through) If the platforms match for serializing and deserializing, there isn't a problem. So that leaves the platforms mismatched on the nature of the hashCode field: serialize 1. -> deserialize 2. // works since defaultReadObject puts zero in the field serialize 2. -> deserialize 1. // works since defaultReadObject ignores the extra field serialize 2. -> deserialize 3. // works since defaultReadObject ignores the extra field serialize 3. -> deserialize 2. // works since defaultReadObject puts zero in the field On that basis, moving to Approved for JDK 16.
22-09-2020

More info in the compatibility section added. Let me know if I need to update something else. Thanks.
22-09-2020

Moving to Provisional, not Approved. Before re-finalizing the request, please include (or cite) a more detailed analysis about why this change is acceptable in practice for both forward and backward compatibility.
21-09-2020