Apparent bug in javac's dead code elimination

Vicente-Arturo Romero-Zaldivar vicente.romero at oracle.com
Mon Sep 8 18:58:56 UTC 2014


Hi Chris,

Yes I agree with Jon, this is not a bug. I have been checking the patch 
for [1] see [2]. In the code you pasted Cake$1 has nothing to do with 
the inner class defined in the 'if (false)' sentence. It's just javac 
reusing an existing class name that would have been a 'real' class if 
the condition were different to false. What do you need this class for? 
You shouldn't need to deal with it. The problem is that we don't have a 
way to say: "hey this is not a common inner class this is just a token, 
exact term access constructor tag, to provide access to a private 
constructor". The only difference between a token and a 'common' inner 
class is that the token is empty, no methods in it.

Thanks,
Vicente

[1] https://bugs.openjdk.java.net/browse/JDK-7199823
[2] http://hg.openjdk.java.net/jdk8/jdk8/langtools/rev/a51a8dac0a2f



On 09/08/2014 10:26 AM, Jonathan Gibbons wrote:
> 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