Bug 117758 - [compiler] private dropped from inner class constructor
Summary: [compiler] private dropped from inner class constructor
Status: VERIFIED FIXED
Alias: None
Product: JDT
Classification: Eclipse Project
Component: Core (show other bugs)
Version: 3.1   Edit
Hardware: PC Windows 2000
: P3 normal (vote)
Target Milestone: 3.2 M6   Edit
Assignee: Philipe Mulet CLA
QA Contact:
URL:
Whiteboard:
Keywords:
: 77473 (view as bug list)
Depends on:
Blocks:
 
Reported: 2005-11-23 12:48 EST by Art Dyer CLA
Modified: 2006-03-28 06:15 EST (History)
2 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Art Dyer CLA 2005-11-23 12:48:41 EST
The "private" modifier is dropped from constructors of private inner classes (both default and explicitly declared ones).  Perhaps this was a deliberate choice since it eliminates the need for a synthetic constructor, but it causes the following class to behave differently than it does if compiled with javac.  

import java.lang.reflect.Constructor;
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ObjectInputStream;
import java.io.Serializable;

public class Foo
{
  public static void main(String[] args) throws Throwable
  {
    Constructor[] ctors = Bar.class.getDeclaredConstructors();
    for (int i = 0; i < ctors.length; ++i)
    {
      System.out.println(ctors[i]);
    }

    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    ObjectOutputStream oos = new ObjectOutputStream(baos);
    oos.writeObject(new Zot());
    oos.close();

    ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
    ObjectInputStream ois = new ObjectInputStream(bais);
    System.out.println(ois.readObject());
    
  }

  private static class Bar
  {
    private Bar()
    {
    }
  }

  private static class Zot extends Bar implements Serializable
  {
  }
}

If the inner class Bar is changed to default access while leaving its constructor private, the behavior is then equivalent to javac's.
Comment 1 Olivier Thomann CLA 2005-11-23 14:02:10 EST
We reset the private bit in MethodScope line 124.
Kent, is this required?
Comment 2 Kent Johnson CLA 2005-11-24 15:44:08 EST
Philippe, do you remember why we did this?
Comment 3 Philipe Mulet CLA 2005-11-25 16:27:57 EST
This indeed was done to simplify innerclass synthetic access emulation. Note that the synthetic emulation is spec'ed nowhere, thus our decision is as valid as javac's. There are numerous cases where our synthetic constructions will not match theirs, and this is only one of them.

Comment 4 Art Dyer CLA 2005-11-28 11:42:14 EST
Your synthetics certainly don't need to match javac's, but that isn't the issue.  An explicitly declared constructor has had its access modifier changed. (Or, if a default constructor has to be generated, its access does not match its class as JLS 8.8.7 requires.)

This is a pretty small point, unlikely to cause any real trouble if you decide to leave it alone.  But I still think it's a bug and not just a matter that the spec is silent on.
Comment 5 Tom Tromey CLA 2006-02-15 12:35:49 EST
We ran into this problem when compiling Mauve with the eclipse
compiler.  It does affect serialization.  (In our particular case it
occurred with an implicit constructor, but the idea is the same...)

FWIW all the other compilers I know of (jikes, gcj, gcjx, javac) generate
a private constructor as specified, and then a package-private
accessor constructor.
Comment 6 Philipe Mulet CLA 2006-02-15 15:55:43 EST
This behavior is optional, but always occurring as soon as compliance >= 1.3. I believe we did match some behavior back then. Maybe this is obsolete nowadays (need to check further). Note that our emulation is more efficient in those case, but I agree it clears a private modifier which isn't super nice.

Now, Tom, I wonder why you mention serialization. There is no way you can assume serializing from on compiler to another one. There are too many subtle differences, like this one. Unless internals were spec'ed, serialization must ensure using the same reference on both ends, or you are asking for trouble.
Comment 7 Tom Tromey CLA 2006-02-15 16:09:56 EST
Yeah, I understand about relying on serialization compatibility
across compilers.  And the issue is probably a minor one as it
requires a serializable member class with a private constructor.
And yet we managed to hit this :-)

But suppose you do understand serialization compatibility and are
trying to make it work -- ie you specify serialVersionUID.
In this case changing a constructor from private to package-private
could break things, because (as I understand it) it changes which
constructor is invoked when deserializing.

Our test case looks a lot like the test in this PR, but it had
an implicit constructor.  I think we (meaning mauve/classpath/etc
hackers) all had the same interpretation of the JLS, namely that
the implicit constructor would take its access from its declaring
class -- not just in the compiler's view but in the generated class file.
Comment 8 Philipe Mulet CLA 2006-03-01 05:22:42 EST
*** Bug 77473 has been marked as a duplicate of this bug. ***
Comment 9 Philipe Mulet CLA 2006-03-01 05:24:56 EST
After investigation, I must apologize to all. Indeed, the behavior I was describing was only meant to occur within local types, to ease emulation (and I will further check this again).

Changing visibility for member type constructors feel totally wrong, and I will fix it asap. Thanks for the reminder.
Comment 10 Philipe Mulet CLA 2006-03-01 12:59:49 EST
Added InnerEmulationTest#test124
Comment 11 Philipe Mulet CLA 2006-03-02 06:58:22 EST
Checked the local type scenario. I aligned our behavior with javac. At 1.3 compliance, the private modifier on local type constructor is kept, and emulation is performed. From 1.4 on, it is cleared. 
Added InnerEmulationTest#test125.

Fixed.
Comment 12 David Audel CLA 2006-03-28 06:15:29 EST
Verified for 3.2 M6 using build I20060328-0010