Apparent bug in javac's dead code elimination

Jonathan Gibbons jonathan.gibbons at oracle.com
Mon Sep 8 17:26:05 UTC 2014


I believe the existence of Cake$1 is part of javac's handling of 
allowing any private code in SomeClass to access any private code in 
SomeOtherClass.

-- Jon

On 09/06/2014 08:06 PM, 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