Bug 30209 - JDT compiler bytecode incompatibility with JDK bytecode results in serialization error
Summary: JDT compiler bytecode incompatibility with JDK bytecode results in serializa...
Status: RESOLVED DUPLICATE of bug 10104
Alias: None
Product: JDT
Classification: Eclipse Project
Component: Core (show other bugs)
Version: 2.1   Edit
Hardware: PC Windows 2000
: P3 normal (vote)
Target Milestone: 2.1 M5   Edit
Assignee: JDT-Core-Inbox CLA
QA Contact:
URL:
Whiteboard:
Keywords:
: 30357 32227 37328 (view as bug list)
Depends on:
Blocks:
 
Reported: 2003-01-24 16:04 EST by Eldon Metz CLA
Modified: 2003-09-05 07:54 EDT (History)
3 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Eldon Metz CLA 2003-01-24 16:04:28 EST
The following test class reproduces the problem with the serialization 
compatibility between the JDT compiler and the JDK 1.4 and JDK 1.3 compilers.

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

/**
 * This is a test class that illustrates a serialization incompatibility
 * between the eclipse and standard JDK compiler implementations.  The problem
 * manifests itself when trying to serialize bytecode compiled with one
 * compiler and then trying to deserialize it with bytecode compiled from
 * another compiler
 * 
 * @author eldonm
 */
public class SerializationTest implements Serializable {

    // (1) The SerializationTest.class reference is what is causing the problem
    private static String className = SerializationTest.class.getName();

    /**
     * Constructor for SerializationTest.
     */
    public SerializationTest() {
        super();
    }

    public static void main(String[] args) throws Exception {
        if (args.length != 2) {
            printUsage();
            System.exit(1);
        }

        if ("write".equals(args[0])) {
            SerializationTest test = new SerializationTest();
            ObjectOutputStream out = new ObjectOutputStream(
                new FileOutputStream(args[1]));
            out.writeObject(test);
            out.close();
        } else if ("read".equals(args[0])) {
            ObjectInputStream in = new ObjectInputStream(
                new FileInputStream(args[1]));
            SerializationTest test = (SerializationTest) in.readObject();
            in.close();
        } else {
            printUsage();
        }
    }

    static void printUsage() {
        System.out.println(
            "Usage: java SerializationTest write|read filename");
    }
}

To reproduce the problem, add this class to a java natured Eclipse project and 
execute it as a application after adding the parameters "write"  followed by 
the desired serialized file name of the test serialized file 
(e.g. "c:\\test.ser")

Then compile the same code with the JDK 1.3 or 1.4 compiler and execute the 
application with the read argument on the same test filename (e.g. java read 
c:\\test.ser").  

You will then see a stack trace similar to the following:

java.io.InvalidClassException: SerializationTest; local class incomp
atible: stream classdesc serialVersionUID = -6933307971724216872, local class 
se
rialVersionUID = -5182857768810774092
  at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java
:459)
  at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.
java:1521)
  at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.jav
a:1435)
  at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStrea
m.java:1626)
  at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:
1274)
  at java.io.ObjectInputStream.readObject(ObjectInputStream.java:3
24)
  at SerializationTest.main(Unknown Source)

The (1) comment in the code above is the cause of the problem.  The static 
string is assigned a value from the class using the java.lang.Class.getName() 
method.  This triggers the eclipse compiler to synthesize additional class 
structure as shown below (taken from the decompiled version of the bytecode 
for the class listing shown above):

// JDT

  private static String className;
  static Class class$0; /* synthetic field */

  static
  {
    className = SerializationTest.class.getName();
  }

// JDK 1.4

    static Class class$(String s)
    {
        return Class.forName(s);
        ClassNotFoundException classnotfoundexception;
        classnotfoundexception;
        throw new NoClassDefFoundError(classnotfoundexception.getMessage());
    }

    private static String className;
    static Class class$SerializationTest; /* synthetic field */

    static
    {
        className = (class$SerializationTest != null ?
class$SerializationTest : (class$SerializationTest =
class$("SerializationTest"))).getName();
    }

This difference between the two results in a different class signature, thus   
resulting in the serialization incompatibility included at the beginning of 
this report.
Comment 1 Philipe Mulet CLA 2003-01-24 18:09:15 EST
Unfortunately, the Java language specs does specify one particular 
implementation for the class literal. Our implementation is different from 
others, causing the serialization issues you are seeing; but none is more right 
than the other. Ours is generating more compact code (one method less).

If the specs were enforcing one particular implementation to be the proper one, 
then we would follow it. On the other end, serialization should ignore 
synthetics, and it doesn't thus this is a dead-end.

Your best take is to use the same binaries on the way in and out. Either use 
binaries from JDT or from a different compiler on both ends.

Similar issues arise with local innerclasses.

At last, you could resort to using the serialVersionUID trick to workaround 
these issues.
Comment 2 Philipe Mulet CLA 2003-01-24 18:10:57 EST
Oops, my previous post should read: 

Unfortunately, the Java language specs does NOT specify one particular 
implementation for the class literal.
Comment 3 Eldon Metz CLA 2003-01-24 18:28:44 EST
Thanks for the detailed information and rapid response!  When we ran into this 
a few weeks ago, we reverted to just using JDT compiled code everywhere.

Has anyone pushed on Sun to improve serialization to ignore synthetics, or to 
at least remove this ambiguity in the specification?

It seems, from my perspective, that even though the JDT code is a bit more 
compact, it would be great (for portability) to mimic what Sun has been doing 
until the specification or serialization issue can be worked out.

Our primary concern was ensuring automated nightly builds using ant produced 
the same bytecode the development machines produced so that a complex 
deployment of a multi-tier system where serialization is used to move data 
between tiers would succeed.  We successfully worked around the issue by 
replicating the eclipse build process using ant with the build.compiler 
property set to org.eclipse.jdt.core.JDTCompilerAdapter.  It would have been 
extremely beneficial if Eclipse included a Export to Ant build script feature 
or a way to execute Eclipse in headless mode from the command-line, as we had 
to replicate the eclipse setup manually in the ant build script.
Comment 4 Philipe Mulet CLA 2003-01-25 07:23:17 EST
Eclipse can certainly be run in headless mode, and in particular the JDT/Core 
component will run under these conditions (no dependency on UI code). You could 
setup a workspace in headless mode (our test suites are precisely doing it, you 
could check org.eclipse.jdt.core.tests.model project on dev.eclipse.org).

As for getting Sun to spec either serialization or compiler internals, I 
remember hearing one of the language guru clearly saying no. This is very 
unfortunate, and trying to mimic some unspecified behavior is hard (adjusting 
on a moving target). 

I think using the same binaries on both ends is the only true answer. 
FYI, we recently made a couple fixes to our compiler which could result in some 
serialization issues with respect to serialized instances of local innerclasses 
(we posted it on jdt-core-dev mailing list). This to say that binaries have to 
be produced with the exact same compiler to be trustable (same vendor, same 
version) to be reliable.

Hope this helps, and anyway thanks for your original diagnosis of this issue.
Comment 5 Philipe Mulet CLA 2003-01-27 09:42:03 EST
Won't change current implementation unless spec would.
Comment 6 Philipe Mulet CLA 2003-01-28 06:21:50 EST
*** Bug 30357 has been marked as a duplicate of this bug. ***
Comment 7 Philipe Mulet CLA 2003-02-19 08:27:08 EST
*** Bug 32227 has been marked as a duplicate of this bug. ***
Comment 8 Olivier Thomann CLA 2003-05-07 11:54:53 EDT
*** Bug 37328 has been marked as a duplicate of this bug. ***
Comment 9 Philipe Mulet CLA 2003-09-05 07:54:13 EDT
Should have been tagged as duplicate
Comment 10 Philipe Mulet CLA 2003-09-05 07:54:44 EDT

*** This bug has been marked as a duplicate of 10104 ***