Apparent bug in javac's dead code elimination

Maurizio Cimadamore maurizio.cimadamore at oracle.com
Mon Sep 8 13:33:07 UTC 2014


Hi Chris,
we did some work as part of this:

https://bugs.openjdk.java.net/browse/JDK-7199823

When working on JDK 8. While this fixes the main issue, it seems like 
there are other related issues that we need to address in this area. 
Thanks for the report!

Maurizio

On 07/09/14 04:06, Chris Kitching wrote:
> Greetings,
>
> As you probably know, javac does a bit of dead code elimintion: it'll
> get rid of redundant constructs like if (false) and suchlike.
>
> Consider, then, this ridiculous example:
>
> public class Cake {
>      public void start() {
>          if (false) {
>              new Runnable() {
>                  @Override
>                  public void run() {
>                      // Whatever
>                  }
>              };
>          }
>      }
>
>      private static class SomeClass {
>      }
> }
>
> As you might expect, this produces two classes: Cake and
> Cake$SomeClass.class, and all is well.
>
> Now consider this slightly more ridiculous example:
>
> public class Cake {
>      public void start() {
>          if (false) {
>              new Runnable() {
>                  @Override
>                  public void run() {
>                      // Whatever
>                  }
>              };
>          }
>      }
>
>      private static class SomeClass {
>      }
>
>      private static class SomeOtherClass extends SomeClass {
>      }
> }
>
>
> All I did was add an empty SomeOtherClass extending SomeClass.
> This example produces out these classes:
>
> Cake$1
> Cake
> Cake$SomeClass
> Cake$SomeOtherClass
>
> Cake$1, the Runnable wrapped in `if (false)`, has suddenly appeared.
> Oddly, the bytecode in Cake$1 does not change as you alter the contents
> of run(). It's as if javac is omitting the contents of the methods of
> the class, but not the class itself (but it did so earlier before we
> added SomeOtherClass!)
> Cake$1 is also corrupt. Attempts to load the class using the Reflection
> API (because, you know, that's a sane thing to do) fail with a
> stacktrace like this:
>
> java.lang.ClassFormatError: Absent Code attribute in method that is not
> native or abstract in class file
> org/mozilla/gecko/animation/PropertyAnimator$1
>   	at java.lang.ClassLoader.defineClass1(Native Method)
>   	at java.lang.ClassLoader.defineClass(ClassLoader.java:791)
>   	at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
>   	at java.net.URLClassLoader.defineClass(URLClassLoader.java:449)
>   	at java.net.URLClassLoader.access$100(URLClassLoader.java:71)
>   	at java.net.URLClassLoader$1.run(URLClassLoader.java:361)
>   	at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
>   	at java.security.AccessController.doPrivileged(Native Method)
>   	at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
>   	at java.lang.ClassLoader.loadClass(ClassLoader.java:423)
>   	at java.lang.ClassLoader.loadClass(ClassLoader.java:356)
>
> And that's exactly what just happened to Mozilla:
> https://bugzilla.mozilla.org/show_bug.cgi?id=1063991
>
> (that also provides a bit of context, as well as the interesting
> consequence that the Cake$1 you get if you move the Runnable outside the
> if is different to the one you get in the situation described above
> (when, really, no Cake$1 at all should exist).
>
> Perhaps the desugaring step for inner classes is getting a little...
> overzealous?
>
> I've been able to reproduce this with javac 7 and 8. I've not tried it
> on the current JDK 9 snapshot.



More information about the compiler-dev mailing list