Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.

Bug 351653

Summary: [1.7][compiler]: VerifyError in try statement with finally and return statements
Product: [Eclipse Project] JDT Reporter: kurt <kurt2002>
Component: CoreAssignee: Olivier Thomann <Olivier_Thomann>
Status: VERIFIED FIXED QA Contact:
Severity: major    
Priority: P3 CC: deepakazad, kurt2002, Olivier_Thomann, srikanth_sankaran
Version: 3.7Flags: srikanth_sankaran: review+
Target Milestone: 3.7.1   
Hardware: PC   
OS: Linux   
Whiteboard:
Attachments:
Description Flags
java-source-example to reproduce the eclipse-compiler-bug
none
Proposed fix + updated regression tests
none
java.lang.VerifyError: Inconsistent stackmap frames at branch target 244 in method E7Bug2.copyDir(Ljava/util/List;ZZZLjava/io/File;Ljava/io/File;Ljava/io/FileFilter;)Ljava/util/List; at offset 236 none

Description kurt CLA 2011-07-10 07:26:10 EDT
Created attachment 199388 [details]
java-source-example to reproduce the eclipse-compiler-bug

Depending on where local variables are declared I get a VerifyError at runtime:

Exception in thread "main" java.lang.VerifyError: Instruction type does not match stack map in method E7Bug.contentEquals(Ljava/nio/file/Path;Ljava/nio/file/Path;)Z at offset 73
	at java.lang.Class.getDeclaredMethods0(Native Method)
	at java.lang.Class.privateGetDeclaredMethods(Class.java:2442)
	at java.lang.Class.getMethod0(Class.java:2685)
	at java.lang.Class.getMethod(Class.java:1620)
	at sun.launcher.LauncherHelper.getMainMethod(LauncherHelper.java:484)
	at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:476)

In the attached example see two the "// --- defining variables here" comments on how to reproduce the bug.

Greetings
Kurt
Comment 1 Srikanth Sankaran CLA 2011-07-10 22:40:25 EDT
What is your build id ? On 3.7/HEAD I don't see this problem.
I do see this issue however on the JAVA7 branch. Are you using
the beta version ? Please confirm. (Also what JRE are you using ?)
Comment 2 Srikanth Sankaran CLA 2011-07-11 01:38:46 EDT
Interestingly, this problem shows up only when compiling with 1.7 level
and does not show up when compiling with source level 1.6.

More curiosly, the code generated is the same in both cases. A comparison
of the 1.6 class file and 1.7 class file shows that only the class file
major version number and check sum are different between the files. Everything
else is the same.

Slightly smaller test case:

import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;

public class E7Bug {

  public boolean contentEquals(final Path src, final Path tar) throws IOException {

    int lrSrc;
    InputStream isSrc = null;
    InputStream isTar = null;
    try {
      isSrc = Files.newInputStream(src);
      if (isSrc == null)
        return false;
      isTar = Files.newInputStream(tar);
      if (isTar == null)
        return false;
      do {
        lrSrc = 1;
      } while ((lrSrc >= 0));
    } finally {
        System.out.println("Finally");
    }
    return true;
  }
  public static void main(final String[] args) {
    System.out.println("Done");
  }
}
Comment 3 Srikanth Sankaran CLA 2011-07-11 01:57:08 EDT
(In reply to comment #1)
> What is your build id ? On 3.7/HEAD I don't see this problem.

My mistake, I do see this problem on 3.7/HEAD, but only at 1.7
compliance. How did you set choose 1.7 compliance in 3.7 since
it doesn't support choosing this level ? (there are other means to
select 1.7, but checking your scenario).

(In reply to comment #2)
> Interestingly, this problem shows up only when compiling with 1.7 level
> and does not show up when compiling with source level 1.6.
> 
> More curiosly, the code generated is the same in both cases. A comparison
> of the 1.6 class file and 1.7 class file shows that only the class file
> major version number and check sum are different between the files. Everything
> else is the same.

If I binary edit the 1.6 class file and set the major version number
to 1.7 (51), I get the verify error. Conversely, if I start with the
1.7 class file which fails to verify and change just the major version
number and set it to 1.6 (50), it starts to verify fine.

So this looks like a case of the VM tightening its behavior on code
that used to verify alright earlier.

Under investigation.
Comment 4 Srikanth Sankaran CLA 2011-07-11 02:26:24 EDT
At the outset this looks a problem in code generation for nio related
classes.

The local variable table looks odd:

        Start  Length  Slot  Name   Signature
               0      81     0  this   LE7Bug;
               0      81     1   src   Ljava/nio/file/Path;
               0      81     2   tar   Ljava/nio/file/Path;
              21      10     3 lrSrc   I
              51       7     3 lrSrc   I
              71      10     3 lrSrc   I
               3      78     4 isSrc   Ljava/io/InputStream;
               6      75     5 isTar   Ljava/io/InputStream;

See the multiple entries for lrSrc - at first glance, this doesn't seem
to make sense.
Comment 5 Srikanth Sankaran CLA 2011-07-11 02:45:23 EDT
(In reply to comment #4)
> At the outset this looks a problem in code generation for nio related
> classes.

Not connected to nio classes at all. Here is a further simplified example:

public class X {
  public static void main(String[] args) {
	    int lrSrc;
	    Object isSrc = null, isTar = null;
	    try {
	      if (isSrc == null)
	        return;
	      if (isTar == null)
	        return;
	      do {
	        lrSrc = 1;
	      } while ((lrSrc >= 0));
	    } finally {
	        lrSrc = 0;
	    }
  }
}
Comment 6 Srikanth Sankaran CLA 2011-07-11 03:15:26 EDT
Even smaller test:

public class X {
    public static void main(String[] p) {
	    int i;
	    try {
	      if (p == null || p == null)
	        return;
	      i = 0;
	    } finally {
	        i = 0;
	    }
    }
}
Comment 7 Srikanth Sankaran CLA 2011-07-11 03:31:51 EDT
(In reply to comment #6)

Generated code for test case in comment#6 is:

  public static void main(java.lang.String[]);
    flags: ACC_PUBLIC, ACC_STATIC

    Code:
      stack=1, locals=3, args_size=1
         0: aload_0       
         1: ifnull        8
         4: aload_0       
         5: ifnonnull     11
         8: iconst_0      
         9: istore_1      
        10: return        
        11: iconst_0      
        12: istore_1      
        13: goto          21
        16: astore_2      
        17: iconst_0      
        18: istore_1      
        19: aload_2       
        20: athrow        
        21: iconst_0      
        22: istore_1      
        23: return        
      Exception table:
         from    to  target type
             0     8    16   any
            11    16    16   any
      LineNumberTable:
        line 5: 0
        line 9: 8
        line 6: 10
        line 7: 11
        line 8: 16
        line 9: 17
        line 10: 19
        line 9: 21
        line 11: 23
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
               0      24     0     p   [Ljava/lang/String;
               8       3     1     i   I
              13       3     1     i   I
              19       5     1     i   I

Messages from IBM JVM is: 

Exception in thread "main" java.lang.VerifyError: JVMVRFY012 stack shape inconsistent; class=X, method=main([Ljava/lang/String;)V, pc=1
	at java.lang.J9VMInternals.verifyImpl(Native Method)
	at java.lang.J9VMInternals.verify(J9VMInternals.java:77)
	at java.lang.J9VMInternals.initialize(J9VMInternals.java:139)

Message from Oracle JVM is more useful: it says:

Exception in thread "main" java.lang.VerifyError: Inconsistent stackmap frames a
t branch target 8 in method X.main([Ljava/lang/String;)V at offset 1
        at java.lang.Class.getDeclaredMethods0(Native Method)
        at java.lang.Class.privateGetDeclaredMethods(Class.java:2442)
        at java.lang.Class.getMethod0(Class.java:2685)
        at java.lang.Class.getMethod(Class.java:1620)
        at sun.launcher.LauncherHelper.getMainMethod(LauncherHelper.java:484)
        at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:476)

The operand stack is empty when you reach 8 irrespective of whether you
branch to 8 or fall through to 8.

If you initialize i = 0 along with its declaration, verify error goes away.

What gives ?
Comment 8 Srikanth Sankaran CLA 2011-07-11 04:04:05 EDT
(In reply to comment #7)
> (In reply to comment #6)

> If you initialize i = 0 along with its declaration, verify error goes away.
> 
> What gives ?

When i is initialized to 0 the local variable looks like:

      LocalVariableTable:
        Start  Length  Slot  Name   Signature
               0      26     0     p   [Ljava/lang/String;
               2      24     1     i   I

as opposed to the uninitialized case:

      LocalVariableTable:
        Start  Length  Slot  Name   Signature
               0      24     0     p   [Ljava/lang/String;
               8       3     1     i   I
              13       3     1     i   I
              19       5     1     i   I

Similarly, the stack map table when i is initialized to 0 looks like:

      StackMapTable: number_of_entries = 4
           frame_type = 252 /* append */
             offset_delta = 10
        locals = [ int ]
           frame_type = 2 /* same */
           frame_type = 68 /* same_locals_1_stack_item */
          stack = [ class java/lang/Throwable ]
           frame_type = 4 /* same */

as opposed to the uninitialized case:

      StackMapTable: number_of_entries = 4
           frame_type = 252 /* append */
             offset_delta = 8
        locals = [ int ]
           frame_type = 250 /* chop */
          offset_delta = 2
           frame_type = 68 /* same_locals_1_stack_item */
          stack = [ class java/lang/Throwable ]
           frame_type = 252 /* append */
             offset_delta = 4
        locals = [ int ]
Comment 9 Srikanth Sankaran CLA 2011-07-11 05:23:51 EDT
(In reply to comment #4)
> At the outset this looks a problem in code generation for nio related
> classes.
> 
> The local variable table looks odd:

[...]

> See the multiple entries for lrSrc - at first glance, this doesn't seem
> to make sense.

These represent three distinct regions where the variable gets initialized
and goes out of scope - so perhaps there is nothing wrong here.

(In reply to comment #8)

Olivier, I am not familiar with the stack map table. Does the below
look reasonable ?

> Similarly, the stack map table when i is initialized to 0 looks like:
> 
>       StackMapTable: number_of_entries = 4
>            frame_type = 252 /* append */
>              offset_delta = 10
>         locals = [ int ]
>            frame_type = 2 /* same */
>            frame_type = 68 /* same_locals_1_stack_item */
>           stack = [ class java/lang/Throwable ]
>            frame_type = 4 /* same */
> 
> as opposed to the uninitialized case:
> 
>       StackMapTable: number_of_entries = 4
>            frame_type = 252 /* append */
>              offset_delta = 8
>         locals = [ int ]
>            frame_type = 250 /* chop */
>           offset_delta = 2
>            frame_type = 68 /* same_locals_1_stack_item */
>           stack = [ class java/lang/Throwable ]
>            frame_type = 252 /* append */
>              offset_delta = 4
>         locals = [ int ]
Comment 10 Srikanth Sankaran CLA 2011-07-11 05:42:02 EDT
Released (disabled) junit org.eclipse.jdt.core.tests.compiler.regression.TryStatementTest._test074().

(This fails on 1.6 as well, while outside of the IDE, I could see the problem
only with 1.7 - not sure what is causing this difference)
Comment 11 Olivier Thomann CLA 2011-07-11 09:19:46 EDT
The bug comes from the first stack map entry.
      Stack map table: number of frames 5
        [pc: 8, append: {int, Object, Object}]
        [pc: 11, full, stack: {}, locals: {String[], _, Object, Object}]
        [pc: 18, same]
        [pc: 27, same_locals_1_stack_item, stack: {Throwable}]
        [pc: 34, full, stack: {}, locals: {String[], int, Object, Object}]

        [pc: 8, append: {int, Object, Object}]
this means that the local lrSrc is initialized when getting to pc 8. We should have:
        [pc: 8, append: {_, Object, Object}]

The sharing of the inlined subroutine might cause confusion in the local variable table shape. I'll take a look.
If we have:
        [pc: 8, append: {_, Object, Object}]
then in 11, there is nothing to do and we would end up with:
        [pc: 11, same]
...
Comment 12 Srikanth Sankaran CLA 2011-07-11 10:38:11 EDT
(In reply to comment #11)
> The bug comes from the first stack map entry.
>       Stack map table: number of frames 5
>         [pc: 8, append: {int, Object, Object}]
>         [pc: 11, full, stack: {}, locals: {String[], _, Object, Object}]
>         [pc: 18, same]
>         [pc: 27, same_locals_1_stack_item, stack: {Throwable}]
>         [pc: 34, full, stack: {}, locals: {String[], int, Object, Object}]
> 
>         [pc: 8, append: {int, Object, Object}]
> this means that the local lrSrc is initialized when getting to pc 8. We should
> have:
>         [pc: 8, append: {_, Object, Object}]
> 
> The sharing of the inlined subroutine might cause confusion in the local
> variable table shape. I'll take a look.

Thanks. Any idea why this would work alright with JDK6 ? is it just a case
of the verification being tightened at JDK7 ?
Comment 13 Srikanth Sankaran CLA 2011-07-11 10:40:06 EDT
(In reply to comment #11)
> The bug comes from the first stack map entry.
>       Stack map table: number of frames 5
>         [pc: 8, append: {int, Object, Object}]
>         [pc: 11, full, stack: {}, locals: {String[], _, Object, Object}]
>         [pc: 18, same]
>         [pc: 27, same_locals_1_stack_item, stack: {Throwable}]
>         [pc: 34, full, stack: {}, locals: {String[], int, Object, Object}]

Where is this output from BTW ? i.e which tool outputs this ?
Comment 14 Olivier Thomann CLA 2011-07-11 10:43:37 EDT
I would say this is a tightening of bytecode verification. I found one place where some variables from the natural exit state are injected and it looks bogus.
Fixing it failed some tests, but looking at the failures (bytecode output mostly), it looks like the previous code was wrong.
I am running all compiler tests with this patch and I am also looking at recompiling all eclipse SDK with this patch to see if Eclipse can start on JDK7.

If everything is fine, I'll ask for a review. This is a really sensitive area where we must be extremely cautious.
Comment 15 Olivier Thomann CLA 2011-07-11 10:45:00 EDT
(In reply to comment #13)
> Where is this output from BTW ? i.e which tool outputs this ?
This is using our own disassembler in system mode. I almost never use javap to look at bytecode. I prefer our own output.
Comment 16 Olivier Thomann CLA 2011-07-12 12:22:49 EDT
Created attachment 199511 [details]
Proposed fix + updated regression tests

Srikanth, please review.
Basically we cannot reinject locals from the natural exit for all subroutine inlining. This was a mistake made a long time ago and I am surprised nobody found it before.
The state index that we set is used to reinject the desired locals at the right locations.
I also moved the regression tests to the stack map test suite where I think it fits better. That patch passes all existing and new tests + recompilation of the SDK as explained in:
http://jroller.com/andyl/entry/compiler_championship_continued
Comment 17 Srikanth Sankaran CLA 2011-07-13 03:58:46 EDT
(In reply to comment #16)
> Created attachment 199511 [details]
> Proposed fix + updated regression tests
> 
> Srikanth, please review.

AFAICT - the patch looks good.

A couple of nits: 

(1) Java7 disclaimer is missing from copyright of StackMap*Test.java
(2) The longer version of test is skipped for < 1.5 with a comment saying
annotations are used - I didn't find annotations in the test. Varargs are
used though. That said this test anyways runs only for 1.6 and above.

(I had forgotten about 1.6 verifier fallback and that being disallowed
for 1.7 - which explains both why things work at 1.6 as well as the junit
vs IDE difference in behavior)
Comment 18 Olivier Thomann CLA 2011-07-13 08:37:02 EDT
(In reply to comment #17)
> (1) Java7 disclaimer is missing from copyright of StackMap*Test.java
Fixed.

> (2) The longer version of test is skipped for < 1.5 with a comment saying
> annotations are used - I didn't find annotations in the test. Varargs are
> used though. That said this test anyways runs only for 1.6 and above.
I removed the check as the StackMapAttributeTest runs only in 1.6 mode and above.

Released in the BETA_JAVA7 branch.
Comment 19 kurt CLA 2011-07-14 13:36:11 EDT
Sorry that I forgot to add the details about version - but you found the bug pretty soon anyway - congratulations!

Here are my details - just in case:

Eclipse:
Version: 3.7.0
Build id: I20110613-1736

Java 7: 
java version "1.7.0"
Java(TM) SE Runtime Environment (build 1.7.0-b147)
Java HotSpot(TM) Server VM (build 21.0-b17, mixed mode)

OS:
Ubuntu 10.04


Just installed the latest JAVA_BETA_7 plugins and still get the error:

Exception in thread "main" java.lang.VerifyError: Inconsistent stackmap frames at branch target 244 in method E7Bug2.copyDir(Ljava/util/List;ZZZLjava/io/File;Ljava/io/File;Ljava/io/FileFilter;)Ljava/util/List; at offset 236
	at java.lang.Class.getDeclaredMethods0(Native Method)
	at java.lang.Class.privateGetDeclaredMethods(Class.java:2442)
	at java.lang.Class.getMethod0(Class.java:2685)
	at java.lang.Class.getMethod(Class.java:1620)
	at sun.launcher.LauncherHelper.getMainMethod(LauncherHelper.java:484)
	at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:476)

I did not check for a workaround this time.

Code in attachment (E7Bug2.java)

Greetings
Kurt
Comment 20 kurt CLA 2011-07-14 13:37:50 EDT
Created attachment 199688 [details]
java.lang.VerifyError: Inconsistent stackmap frames at branch target 244 in method E7Bug2.copyDir(Ljava/util/List;ZZZLjava/io/File;Ljava/io/File;Ljava/io/FileFilter;)Ljava/util/List; at offset 236

see my last comment no. 19
Comment 21 Olivier Thomann CLA 2011-07-14 13:42:42 EDT
I'll open a new bug report for this one as it is not related to the same bug. The previous one was a bug in the try statement specifically.

Closing as FIXED.
Comment 22 kurt CLA 2011-07-14 13:57:00 EDT
Please let me know the bug-id (and/or include me in the cc-list) - I am just too curious ;-)

Thanks
Kurt
Comment 23 Olivier Thomann CLA 2011-07-14 14:04:29 EDT
(In reply to comment #22)
> Please let me know the bug-id (and/or include me in the cc-list) - I am just
> too curious ;-)
For sure. bug 352145.
Thanks for your awesome test case. Should be fixed for the next refresh.
Comment 24 Deepak Azad CLA 2011-07-19 08:05:57 EDT
Verified with v20110714-1300.