Bug 304048 - Compiled class is different from java compiler (lacking a bridge method) --> SerialVersionUID is different
Summary: Compiled class is different from java compiler (lacking a bridge method) --> ...
Status: CLOSED DUPLICATE of bug 10104
Alias: None
Product: JDT
Classification: Eclipse Project
Component: Core (show other bugs)
Version: 3.6   Edit
Hardware: PC Windows XP
: P3 normal (vote)
Target Milestone: 2.0 M4   Edit
Assignee: Srikanth Sankaran CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2010-02-26 10:49 EST by stefan.murawski CLA
Modified: 2010-03-08 06:23 EST (History)
3 users (show)

See Also:


Attachments
Testcases and class Files (3.72 KB, application/zip)
2010-02-26 10:51 EST, stefan.murawski CLA
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description stefan.murawski CLA 2010-02-26 10:49:42 EST
Build Identifier: 20090619-0625

I created a class that extends an abstract class with a compareTo-Method.
The class that is compiled from eclipse is different from the one i created with the java compiler, resulting in a mismatching serialversionuid.

Reproducible: Always

Steps to Reproduce:
1. Create an Abstract Class that implements Comparable and Serializable:
2.Create an Implementation that overwrites this class:
3. Compile with eclipse
4. Compile with javac
5. The javac compiler adds an extra Method   public bridge synthetic int compareTo(java.lang.Object x0); that is not created by the eclipse Compiler
Comment 1 stefan.murawski CLA 2010-02-26 10:51:16 EST
Created attachment 160322 [details]
Testcases and class Files

The attachment contains my Testclasses(test-fodler) and the compiled classes by eclipse(in the build/eclipse folder)/java (in the build/test folder)
Comment 2 Olivier Thomann CLA 2010-02-26 20:54:06 EST
First of all, if you really care about the serialVersionUID, you should provide it yourself.
There is absolutely no guarantee that Eclipse compiler and javac compiler will generate the same version UID.
Srikanth, please investigate if the bridge method is required there.
Thanks.
Comment 3 Srikanth Sankaran CLA 2010-03-01 01:33:12 EST
Simplified test case that can be cut & pasted into package
explorer:
------------------------------8<--------------------------------
import java.io.Serializable;
public class Test extends AbstractBase<Test> {
    @Override
    public int compareTo(Test pO) {
    	return 1;
    }
}
abstract class AbstractBase<TA extends AbstractBase> implements Serializable, Comparable<TA> {
	public int compareTo(TA pO) {
		return -1;
	}
}
----------------------------------------------------------------

javap Test  (Test.class produced by javac5,6,7)
-----------------------------------------------
public class Test extends AbstractBase{
    public Test();
    public int compareTo(Test);
    public int compareTo(AbstractBase);
    public int compareTo(java.lang.Object);
}


javap Test (Test.class produced by eclipse)
-------------------------------------------

Compiled from "Test.java"
public class Test extends AbstractBase{
    public Test();
    public int compareTo(Test);
    public int compareTo(AbstractBase);
}

So a bridge does appear to be missing. However purely
in terms of control flow, this shouldn't matter since
Test inherits the bridge 

public int compareTo(java.lang.Object) ----> public int compareTo(AbstractBase)
and overrides the latter with its own bridge
public int compareTo(AbstractBase) ----> public int compareTo(Test);

So effectively there is a bridge from
public int compareTo(java.lang.Object)  ---> public int compareTo(Test);

so that the program below prints "Test#CompareTo" as expected
and matching javac in this call stack:

	Test.compareTo(Test) line: 5	
	Test.compareTo(AbstractBase) line: 1	
	Test(AbstractBase<TA>).compareTo(Object) line: 1	
	Test.main(String[]) line: 12	

import java.io.Serializable;
public class Test extends AbstractBase<Test> {
    @Override
    public int compareTo(Test pO) {
    	System.out.println("Test#CompareTo");
    	return 1;
    }
    
    public static void main(String [] args) {
    	Comparable c = new Test();
    	Object o = c;
    	c.compareTo(o);
    }
}
abstract class AbstractBase<TA extends AbstractBase> implements Serializable, Comparable<TA> {
	public int compareTo(TA pO) {
		System.out.println("AbstractBase#CompareTo");
		return -1;
	}
}

Having a single bridge a la javac would 

    -- slightly improve performance
    -- eliminate this gratuitous difference with javac
    -- eliminate the kind of issues that arise via use of reflection APIs
       (see bug#298362)

I'll investigate and see what it would take.
Comment 4 Srikanth Sankaran CLA 2010-03-01 04:06:07 EST
(In reply to comment #2)
> First of all, if you really care about the serialVersionUID, you should provide
> it yourself.
> There is absolutely no guarantee that Eclipse compiler and javac compiler will
> generate the same version UID.

Or that two different versions of javac will generate the same SUID. Or
for that matter that two different versions of eclipse compiler will
generate the same SUID.

Section 4.6 (Stream Unique Identifiers) in "Java Object Serialization 
Specification version 1.5.0" states:

"It is strongly recommended that all serializable classes
explicitly declare serialVersionUID values, since the default
serialVersionUID computation is highly sensitive to class
details that may vary depending on compiler implementations,
and can thus result in unexpected serialVersionUID conflicts
during deserialization, causing deserialization to fail."

> Srikanth, please investigate if the bridge method is required there.
> Thanks.

As the example program in comment#3 shows from a control flow
pov, we are doing the right thing. While it would be ideal to
eliminate this gratuitous difference between javac and ecj, after
an initial investigation it looks like a lot of work for
clearing up something that the specification tolerates/makes 
allowances for and  what the spec anyway warns about as being
the developer's responsibility.

Given the above, I am inclined to close this as WONT_FIX.
Comment 5 Srikanth Sankaran CLA 2010-03-01 04:42:01 EST
This has a long history: See bug#10104, bug#30209, 
bug#32227, bug#30357, bug#37328, bug#50958 (and many many more)
Comment 6 Srikanth Sankaran CLA 2010-03-01 04:43:37 EST

*** This bug has been marked as a duplicate of bug 10104 ***
Comment 7 Remy Suen CLA 2010-03-01 07:18:34 EST
Having the target milestone be 2.0M4 is kind of weird in my opinion.

In any case, is there a reason why the visibility of this bug has been reduced?
Comment 8 Srikanth Sankaran CLA 2010-03-01 07:33:06 EST
(In reply to comment #7)
> Having the target milestone be 2.0M4 is kind of weird in my opinion.

I'll admit it felt very odd setting it to that, for better or worse, that
is the documented process we are following
(http://wiki.eclipse.org/JDT_Core_Committer_FAQ#What_target_milestone_should_be_used.3F) 

> In any case, is there a reason why the visibility of this bug has been reduced?

I think it was submitted that way - Stefan ?
Comment 9 stefan.murawski CLA 2010-03-01 07:41:53 EST
I submitted it as normal, because I know using the serialversionUID is the best practice :). 
In our project we don't and we mix compilers(for Developertests only :)), so I didn't find it critical or blocking.
Comment 10 Srikanth Sankaran CLA 2010-03-01 07:58:51 EST
(In reply to comment #8)

> > In any case, is there a reason why the visibility of this bug has been reduced?
> 
> I think it was submitted that way - Stefan ?

In the defect modification history, there is a record:

Who	When	What	Removed	Added

srikanth_sankaran@in.ibm.com	2010-03-01 04:06:07 EST	 Group		Security_Advisories

so I may have had my finger in the pie without knowing it :)

I'll reset it shortly.
Comment 11 Jay Arthanareeswaran CLA 2010-03-08 06:23:42 EST
Verified for 3.6M6 using build I20100305-1011