Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8290041

ModuleDescriptor.hashCode is inconsistent

XMLWordPrintable

    • b11
    • generic
    • generic
    • Verified

        A DESCRIPTION OF THE PROBLEM :
        The ModuleDescriptor hashCode implementation internally calls a modsHashCode method which assumes that sets iterate in a consistent order. By multiplying each sub hash by 43, the computation isn't commutative, leading to inconsistent results.

        Under some conditions, this can cause two ModuleDescriptor instances which are equal to in fact be reported as not equal. The compareTo method still returns 0 in that case. The likelihood of this occurring is random, since it depends on enum hash codes, which vary between JVM sessions. This can cause hash collisions, resulting in inconsistent set ordering. In particular, the requires modifier sets are most sensitive to this.

        This bug was introduced by https://bugs.openjdk.org/browse/JDK-8275509 which partially addressed the problem. The correct fix is to remove the multiply by 43. Note that the AbstactSet.hashCode method simply adds up the sub hashes without any multiplication step.

        STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
        Construct two ModuleDescriptor instances which have modifier sets which have the same elements but iteration order differs.

        EXPECTED VERSUS ACTUAL BEHAVIOR :
        EXPECTED -
        The hash codes for equal ModuleDescriptors should be the same.
        ACTUAL -
        The hash codes are different:

        551086747
        1012586953


        ---------- BEGIN SOURCE ----------
        import java.lang.module.ModuleDescriptor;

        import java.util.Set;

        public class Bug {
            public static void main(String[] args) throws Exception {
                var md1 = ModuleDescriptor.newModule("test")
                    .opens(Set.of(ModuleDescriptor.Opens.Modifier.SYNTHETIC,
                                  ModuleDescriptor.Opens.Modifier.MANDATED),
                           "a.p1", Set.of("a.m1"))
                    .build();

                var md2 = ModuleDescriptor.newModule("test")
                    .opens(Set.of(ModuleDescriptor.Opens.Modifier.MANDATED,
                                  ModuleDescriptor.Opens.Modifier.SYNTHETIC),
                           "a.p1", Set.of("a.m1"))
                    .build();

                System.out.println(md1.equals(md2));
                System.out.println(md1.compareTo(md2));
                System.out.println(md1.hashCode());
                System.out.println(md2.hashCode());
            }
        }
        ---------- END SOURCE ----------

        CUSTOMER SUBMITTED WORKAROUND :
        In cases where the equals method is affected, the compareTo method can be called instead.

        FREQUENCY : always


              jpai Jaikiran Pai
              webbuggrp Webbug Group
              Votes:
              0 Vote for this issue
              Watchers:
              9 Start watching this issue

                Created:
                Updated:
                Resolved: