Bug 23075

Summary: Wrong compiling of inner classes
Product: [Eclipse Project] JDT Reporter: hiddel <hiddel>
Component: CoreAssignee: Philipe Mulet <philippe_mulet>
Status: VERIFIED FIXED QA Contact:
Severity: normal    
Priority: P3 CC: topi.nieminen
Version: 2.0   
Target Milestone: 2.1 M1   
Hardware: PC   
OS: Windows 2000   
Whiteboard:

Description hiddel CLA 2002-09-02 02:50:58 EDT
Eclipse compiler has a bug and produces wrong bytecode for inner classes. The
point is that reference to enclosing instance in constructor is assigned AFTER
calling constructor of superclass. The result is null pointer exception when you
overriding in the inner class a method called by constructor of superclass. Example:

public class A {
  public int m;
  public void pp() {
     C c = new C(4);
     System.out.println(c.get());
  }
  public static void main(String[] args) {
     A a = new A();
     a.pp();
  }

  class C extends B {
    public C(int x1) {
      super(x1);    
    }
    protected void init(int x1) {
       x = m * x1; // <- NULL POINTER EXCEPTION because of m
    }  
  }
}

class B {
  int x;
  public B(int x1) {
    init(x1);
  }
  protected void init(int x1) {
    x  = x1;
  }
  public int get() {
    return x;
  }
}
Comment 1 Philipe Mulet CLA 2002-09-02 07:27:06 EDT
This is not a compiler bug, but rather a VM limitation since innerclasses have 
existed. The bytecode verification process enforces the first instruction 
inside a constructor to be a constructor invocation (super(...) or this(...)). 
Therefore, internals cannot be initialized for virtual calls which may occur 
indirectly through a super constructor invocation.

This is a limitation in innerclass usage due to a VM constraint.
Javac has the same behavior as we do, there is nothing we can do unfortunately, 
unless the VM specs was to change (which isn't the case).
Comment 2 hiddel CLA 2002-09-02 09:20:11 EDT
It is not true that javac has the same behavior as you do actually. When you use
"-source 1.4" option reference to enclosing instance is initialized BEFORE
constructor of superclass is called. 

This is the difference. In 1.3:

Method A. C(A,int)
   0 aload_0
   1 iload_2
   2 invokespecial #1 <Method B(int)>
   5 aload_0
   6 aload_1
   7 putfield #2 <Field A this$0>
  10 return

In 1.4:

Method A. C(A,int)
   0 aload_0
   1 aload_1
   2 putfield #1 <Field A this$0>
   5 aload_0
   6 iload_2
   7 invokespecial #2 <Method B(int)>
  10 return
Comment 3 Philipe Mulet CLA 2002-09-02 10:00:14 EDT
You are right, this seems to have changed recently (-source 1.4). I even see 
the VM spec mentionning it ($4.8.2).

Thanks for pointing this out, we will move accordingly for 1.4 source mode.
Comment 4 Philipe Mulet CLA 2002-09-02 10:44:23 EDT
Actually, javac -target 1.4 is enough to trigger this behavior, note that 
pre1.4 vms would not accept such bytecodes.
Comment 5 Philipe Mulet CLA 2002-09-02 12:06:15 EDT
Fixed in latest
Comment 6 hiddel CLA 2002-09-02 12:21:00 EDT
The fact that <1.4 vms don't accept this bytecode doesn't mean that 1.4 
compliant compiler shouldn't produce such bytecode. This is the way I 
undestand "1.4 compliant" in Eclipse - use only on >=1.4 vms.
Comment 7 Philipe Mulet CLA 2002-09-03 09:32:42 EDT
This is only partially true, you may toggle the compiler for general 1.4 
compliance, but still manually targeting a 1.1 VM (if you turn off assertions -
source 1.3).

Comment 8 David Audel CLA 2002-09-19 06:31:07 EDT
Verified.
Comment 9 Olivier Thomann CLA 2009-09-29 15:15:13 EDT
*** Bug 290791 has been marked as a duplicate of this bug. ***