Bug 232565 - [1.5][compiler] wrong autoboxing code generation leads to VerifyError at runtime
Summary: [1.5][compiler] wrong autoboxing code generation leads to VerifyError at runtime
Status: VERIFIED FIXED
Alias: None
Product: JDT
Classification: Eclipse Project
Component: Core (show other bugs)
Version: 3.4   Edit
Hardware: PC Windows XP
: P3 normal (vote)
Target Milestone: 3.4.1   Edit
Assignee: Philipe Mulet CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2008-05-16 14:41 EDT by Olivier Thomann CLA
Modified: 2008-08-28 12:21 EDT (History)
1 user (show)

See Also:


Attachments
Proposed patch (55.21 KB, patch)
2008-05-23 13:16 EDT, Philipe Mulet CLA
no flags Details | Diff
Better patch for 3.4.x (7.76 KB, patch)
2008-06-30 12:52 EDT, Philipe Mulet CLA
no flags Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Olivier Thomann CLA 2008-05-16 14:41:54 EDT
Eclipse compiles the following code:
public class X<T> {

	T counter;

	public static void main(String[] args) {
		X<Integer> x = new X<Integer>();
		x.counter = 0;
		System.out.println(Integer.toString(x.counter++));
	}
}

but it creates a VerifyError at runtime:
Exception in thread "main" java.lang.VerifyError: (class: X, method: main signature: ([Ljava/lang/String;)V) Incompatible object argument for function call

This fails as well using jdk6, but passes using jdk7b25.
Comment 1 Philipe Mulet CLA 2008-05-23 12:18:41 EDT
Problem comes from the generic checkcast to Integer being inserted a bit too late in the bytecode. So it comes after the field value got dup'ed on the stack (@24). Hence only the top of the stack gets cast in Integer (@25). The dup value below is not cast, and when unboxed it is not proven to be an Integer any longer.

  // Method descriptor #16 ([Ljava/lang/String;)V
  // Stack: 5, Locals: 2
  public static void main(String[] arg0);
     0  new X [1]
     3  dup
     4  invokespecial X() [17]
     7  astore_1
     8  aload_1
     9  iconst_0
    10  invokestatic Integer.valueOf(int) : Integer [18]
    13  putfield X.counter : Object [24]
    16  getstatic System.out : PrintStream [26]
    19  aload_1
    20  dup
    21  getfield X.counter : Object [24]
    24  dup_x1
    25  checkcast Integer [19]
    28  invokevirtual Integer.intValue() : int [32]
    31  iconst_1
    32  iadd
    33  invokestatic Integer.valueOf(int) : Integer [18]
    36  putfield X.counter : Object [24]
    39  invokevirtual Integer.intValue() : int [32]
    42  invokestatic Integer.toString(int) : String [36]
    45  invokevirtual PrintStream.println(String) : void [40]
    48  return
Comment 2 Philipe Mulet CLA 2008-05-23 12:36:52 EDT
Ideally, we should produce:

  public static void main(String[] args);
     0  new X [1]
     3  dup
     4  invokespecial X() [22]
     7  astore_1 [x]
     8  aload_1 [x]
     9  iconst_0
    10  invokestatic Integer.valueOf(int) : Integer [23]
    13  putfield X.counter : Object [29]
    16  getstatic System.out : PrintStream [31]
    19  aload_1 [x]
    20  dup
    21  getfield X.counter : Object [29]
    24  checkcast Integer [24]
    27  invokevirtual Integer.intValue() : int [37]
    30  dup_x1
    31  iconst_1
    32  iadd
    33  invokestatic Integer.valueOf(int) : Integer [23]
    36  putfield X.counter : Object [29]
    39  invokestatic Integer.toString(int) : String [41]
    42  invokevirtual PrintStream.println(String) : void [45]
    45  return
Comment 3 Philipe Mulet CLA 2008-05-23 12:57:42 EDT
Note that problem also arise in other type of references, e.g.

public class X<T> {
        T counter;
        public static void main(String[] args) {
        	 bar(new X<Integer>());
        	 new Y().foo();
        	 new Y().baz();
        }
        static void bar(X<Integer> x) {
        	x.counter = 0;
            System.out.print(Integer.toString(x.counter++));
        }
}

class Y extends X<Integer> {
	Y() {
		this.counter = 0;
	}
    void foo() {
        System.out.print(Integer.toString(counter++));
    }
    void baz() {
        System.out.println(Integer.toString(this.counter++));
    }
}

should print "000"
Comment 4 Philipe Mulet CLA 2008-05-23 13:01:38 EDT
Added AutoboxingTest#test154-155
Comment 5 Philipe Mulet CLA 2008-05-23 13:12:55 EDT
Induced changes are likely too big for inclusion in 3.4RC3. Also given other Java6 compilers do fail it as well, this is not super critical.
Comment 6 Philipe Mulet CLA 2008-05-23 13:16:50 EDT
Created attachment 101778 [details]
Proposed patch

patch is combined with cleanup suggest in bug 231861#c17
Comment 7 Philipe Mulet CLA 2008-05-23 13:22:28 EDT
Btw javac7 produces a longer/unoptimal sequence:

  // Method descriptor #21 ([Ljava/lang/String;)V
  // Stack: 4, Locals: 5
  public static void main(String[] arg0);
     0  new X [2]
     3  dup
     4  invokespecial X() [3]
     7  astore_1
     8  aload_1
     9  iconst_0
    10  invokestatic Integer.valueOf(int) : Integer [4]
    13  putfield X.counter : Object [5]
    16  getstatic System.out : PrintStream [6]
    19  aload_1
    20  astore_2
    21  aload_2
    22  getfield X.counter : Object [5]
    25  checkcast Integer [7]
    28  astore_3
    29  aload_2
    30  aload_2
    31  getfield X.counter : Object [5]
    34  checkcast Integer [7]
    37  invokevirtual Integer.intValue() : int [8]
    40  iconst_1
    41  iadd
    42  invokestatic Integer.valueOf(int) : Integer [4]
    45  dup_x1
    46  putfield X.counter : Object [5]
    49  astore 4
    51  aload_3
    52  invokevirtual Integer.intValue() : int [8]
    55  invokestatic Integer.toString(int) : String [9]
    58  invokevirtual PrintStream.println(String) : void [10]
    61  return
Comment 8 Philipe Mulet CLA 2008-05-23 17:12:05 EDT
Added AutoboxingTest#test156-158
Comment 9 Philipe Mulet CLA 2008-05-26 06:38:16 EDT
Comment on attachment 101778 [details]
Proposed patch

The proposed patch does not quite work. More work is needed.
Comment 10 Philipe Mulet CLA 2008-06-30 12:52:41 EDT
Created attachment 106131 [details]
Better patch for 3.4.x
Comment 11 Philipe Mulet CLA 2008-06-30 15:34:16 EDT
Simpler solution now produces:

  // Method descriptor #21 ([Ljava/lang/String;)V
  // Stack: 5, Locals: 2
  public static void main(String[] args);
     0  new X [1]
     3  dup
     4  invokespecial X() [22]
     7  astore_1 [x]
     8  aload_1 [x]
     9  iconst_0
    10  invokestatic Integer.valueOf(int) : Integer [23]
    13  putfield X.counter : Object [29]
    16  getstatic System.out : PrintStream [31]
    19  aload_1 [x]
    20  dup
    21  getfield X.counter : Object [29]
    24  checkcast Integer [24]
    27  dup_x1
    28  invokevirtual Integer.intValue() : int [37]
    31  iconst_1
    32  iadd
    33  invokestatic Integer.valueOf(int) : Integer [23]
    36  putfield X.counter : Object [29]
    39  invokevirtual Integer.intValue() : int [37]
    42  invokestatic Integer.toString(int) : String [41]
    45  invokevirtual PrintStream.println(String) : void [45]
    48  return
Comment 12 Philipe Mulet CLA 2008-06-30 18:11:35 EDT
Also added AutoboxingTest#test160-164
Released for 3.3.x maintenance branch.
Comment 13 Philipe Mulet CLA 2008-06-30 18:17:06 EDT
Released for 3.4.1
Released for 3.5M1
Fixed
Comment 14 Olivier Thomann CLA 2008-08-06 14:25:20 EDT
Verified for 3.5M1 using I20080805-1307
Comment 15 Frederic Fusier CLA 2008-08-28 12:21:10 EDT
Verified for 3.4.1 using build M20080827-2000.