United StatesChange Country, Oracle Worldwide Web Sites Communities I am a... I want to...
JDK-4365406 : Regression: Calculation of serialVersionUID changed from 1.3 to 1.4

Details
Type:
Bug
Submit Date:
2000-08-24
Status:
Resolved
Updated Date:
2000-10-31
Project Name:
JDK
Resolved Date:
2000-10-17
Component:
core-libs
OS:
solaris_2.6,windows_nt
Sub-Component:
java.text
CPU:
x86,sparc
Priority:
P3
Resolution:
Fixed
Affected Versions:
1.4.0
Fixed Versions:
1.4.0 (beta)

Related Reports
Duplicate:

Sub Tasks

Description

Name: ooR10001			Date: 08/24/2000


Serialization of java.text.AttributedCharacterIterator.Attribute class has 
compatibility problem because serialVersionUID for this class has been changed 
from kestrel to merlin:

%/set/java/jdk1.4/solaris/bin/serialver 
java.text.AttributedCharacterIterator.Attribute
java.text.AttributedCharacterIterator.Attribute:    static final long 
serialVersionUID = -1514471214376796190L;
%/set/java/jdk1.3/solaris/bin/serialver 
java.text.AttributedCharacterIterator\$Attribute
java.text.AttributedCharacterIterator$Attribute:    static final long 
serialVersionUID = -9142742483513960612L;
%

======================================================================

                                    

Comments
CONVERTED DATA

BugTraq+ Release Management Values

COMMIT TO FIX:
merlin-beta

FIXED IN:
merlin-beta

INTEGRATED IN:
merlin-beta


                                     
2004-06-14
EVALUATION

The Attribute class hasn't changed since May 1999. Further testing shows that the problem is in the calculation of the serialVersionUID itself - the result depends on the version of the tool, but not on the version of the class or how the class name is specified:

% /usr/local/java/jdk1.3/solaris/bin/serialver -classpath /usr/local/java/jdk1.3/solaris/jre/lib/rt.jar java.text.AttributedCharacterIterator\$Attribute
java.text.AttributedCharacterIterator$Attribute:    static final long serialVersionUID = -9142742483513960612L;
% /usr/local/java/jdk1.3/solaris/bin/serialver -classpath /usr/local/java/jdk1.4/solaris/jre/lib/rt.jar java.text.AttributedCharacterIterator\$Attribute
java.text.AttributedCharacterIterator$Attribute:    static final long serialVersionUID = -9142742483513960612L;
% /usr/local/java/jdk1.4/solaris/bin/serialver -classpath /usr/local/java/jdk1.3/solaris/jre/lib/rt.jar java.text.AttributedCharacterIterator\$Attribute
java.text.AttributedCharacterIterator$Attribute:    static final long serialVersionUID = -1514471214376796190L;
% /usr/local/java/jdk1.4/solaris/bin/serialver -classpath /usr/local/java/jdk1.4/solaris/jre/lib/rt.jar java.text.AttributedCharacterIterator\$Attribute
java.text.AttributedCharacterIterator$Attribute:    static final long serialVersionUID = -1514471214376796190L;
% /usr/local/java/jdk1.4/solaris/bin/serialver -classpath /usr/local/java/jdk1.3/solaris/jre/lib/rt.jar java.text.AttributedCharacterIterator.Attribute
java.text.AttributedCharacterIterator.Attribute:    static final long serialVersionUID = -1514471214376796190L;
% /usr/local/java/jdk1.4/solaris/bin/serialver -classpath /usr/local/java/jdk1.4/solaris/jre/lib/rt.jar java.text.AttributedCharacterIterator.Attribute
java.text.AttributedCharacterIterator.Attribute:    static final long serialVersionUID = -1514471214376796190L;

norbert.lindenberg@Eng 2000-08-24


The conclusion drawn in the above comment is not accurate.  The class
java.text.AttributedCharacterIterator$Attribute is loaded from the
bootclasspath; hence, specifying -classpath does not actually cause it to be
loaded from the intended location.  In other words, in the tests above, the 1.3
version of serialver loaded java.text.AttributedCharacterIterator$Attribute
from /usr/local/java/jdk1.3/solaris/jre/lib/rt.jar, while the 1.4 version of
serialver loaded it from /usr/local/java/jdk1.4/solaris/jre/lib/rt.jar.

The reason this is significant is that the class files for the 1.3 and 1.4
versions of java.text.AttributedCharacterIterator$Attribute differ even though
the source did not change between 1.3 and 1.4.  This change is due to
differences between the 1.3 and 1.4 versions of javac (I've appended a detailed
explanation to the end of this comment, for the terminally curious).
These differences between the 1.3 and 1.4 generated code for
AttributedCharacterIterator$Attribute affect the serialVersionUID computation
and result in a different serialVersionUID value for the two versions of the
class.  

The (unfortunate) sensitivity of the serialVersionUID computation to synthetic
class elements that may be generated differently by different compilers has
already been noted in bug 4169272 ("serialVersionUID computation can be
unreliable across releases & implementations").  Though the serialVersionUID
computation algorithm is known to be flawed, it cannot be changed at this point
without breaking compatibility with past JDKs.  It's unreasonable to ask that
javac be changed to revert to its 1.3 behavior since the code that 1.4 javac
generates is still valid with respect to the JLS/JVMS.  The only possible work
around (as noted in the bug report for 4169272) is for
java.text.AttributedCharacterIterator$Attribute to declare an explicit
serialVersionUID (equal in value to the 1.3 default).  Therefore, I'm refiling
this bug under java/classes_text.

(An explanation of the generated code change between 1.3 and 1.4 javac
follows:)

To allow for lazy resolution of classes, explicit ".class" references in java
source files are compiled into synthetic field/methods pairs.  For example, the
following java source:

    class Foo {
        void func() { Class cl = Integer.class; }
    }

...results in the following class file in both 1.3 and 1.4 (javap output
shown):

    class Foo extends java.lang.Object {
        static java.lang.Class class$java$lang$Integer;
        Foo();
        void func();
        static java.lang.Class class$(java.lang.String);
    }

Note that two synthetic class members--the static field class$java$lang$Integer
and static method class$()--have been added by javac.  The static field is used
to hold the value of Integer.class once it has been obtained (lazily); the
static method is used to look up Integer.class via a call to Class.forName():
    
    Method void func()
       0 getstatic #7 <Field java.lang.Class class$java$lang$Integer>
       3 ifnonnull 18
       6 ldc #8 <String "java.lang.Integer">
       8 invokestatic #9 <Method java.lang.Class class$(java.lang.String)>
      11 dup
      12 putstatic #7 <Field java.lang.Class class$java$lang$Integer>
      15 goto 21
      18 getstatic #7 <Field java.lang.Class class$java$lang$Integer>
      21 astore_1
      22 return
    
    Method java.lang.Class class$(java.lang.String)
       0 aload_0
       1 invokestatic #1 <Method java.lang.Class forName(java.lang.String)>
       4 areturn
       5 astore_1
       6 new #3 <Class java.lang.NoClassDefFoundError>
       9 dup
      10 aload_1
      11 invokevirtual #4 <Method java.lang.String getMessage()>
      14 invokespecial #5 <Method java.lang.NoClassDefFoundError(java.lang.Strin
g)>
      17 athrow
    Exception table:
       from   to  target type
         0     5     5   <Class java.lang.ClassNotFoundException>

Things get trickier when an explicit ".class" reference occurs within a nested
class.  In 1.3, the following code:

    class Foo {
        class Bar {
            void func() { Class cl = Integer.class; }
        }
    }

Compiles into the following two classes:

    class Foo extends java.lang.Object {
        Foo();
        class Foo. Bar extends java.lang.Object 
        /* ACC_SUPER bit NOT set */
    {
            static java.lang.Class class$java$lang$Integer;
            Foo.Bar(Foo);
            void func();
            static java.lang.Class class$(java.lang.String);
        }
    }

Note that the synthetic static field and method are added to the inner class.
1.4 javac compiles the same code into the following classes:

    class Foo extends java.lang.Object {
        static java.lang.Class class$java$lang$Integer;
        Foo();
        static java.lang.Class class$(java.lang.String);
        class Foo. Bar extends java.lang.Object 
        /* ACC_SUPER bit NOT set */
    {
            Foo.Bar(Foo);
            void func();
        }
    }

Note that the synthetic static field/method have been added to the outer class,
instead of the inner class.  This change in itself is enough to affect
serialVersionUID values if either Foo or Foo$Bar happen to be serializable (the
serialVersionUID computation involves the signatures of all fields except those
that are private static or private transient, as well as the signatures of all
non-private methods).  In the case of
java.text.AttributedCharacterIterator$Attribute, this discrepancy is pushed a
step further: since the outer entity (java.text.AttributedCharacterIterator) is
an interface, it cannot accomodate the sythetic field/method pair that javac
wishes to add; consequently, the synthetic members are placed in a new
synthetic class.  To illustrate, the code:

    interface Foo {
        class Bar {
            void func() { Class cl = Integer.class; }
        }
    }

...gets compiled into the following *three* classes:

    interface Foo 
        /* ACC_SUPER bit NOT set */
    {
        public static class Foo. Bar extends java.lang.Object 
        /* ACC_SUPER bit NOT set */
    {
            public Foo.Bar();
            void func();
        }
    }
    
    class Foo$1 extends java.lang.Object {
        static java.lang.Class class$java$lang$Integer;
        static java.lang.Class class$(java.lang.String);
    }

1.3 javac does not need to perform this extra transformation since it adds the
synthetic field/method pair to the inner class, which can legally accomodate
the members.  Needless to say, the transformations effected by 1.4 javac also
result in changed serialVersionUID values (if the affected classes happen to be
serializable).

michael.warres@east 2000-09-15


This change in the compiled binary and the resulting change in the serial version represents a source incompatibility - recompiling existing sources may no longer result in binaries that behave as before. Steve.Wilson@eng decided that this source incompatibility is acceptable, and it will be documented in the Merlin compatibility documentation. The Attribute class will receive an explicit serialVersionUID in order to remain compatible with 1.3.
norbert.lindenberg@Eng 2000-10-04
                                     
2000-10-04
SUGGESTED FIX

java.text.AttributedCharacterIterator$Attribute should declare a
serialVersionUID value equal to the default value under 1.3/1.2:

  private static final long serialVersionUID = -9142742483513960612L;

See evaluation for details.

michael.warres@east 2000-09-15
                                     
2000-09-15



Hardware and Software, Engineered to Work Together