Apparent bug in javac's dead code elimination

Chris Kitching chriskitching at linux.com
Mon Sep 8 19:27:03 UTC 2014


Hello,

"What do you need this class for?"

I don't need this class for anything, I merely need the compiler to
promise to never emit classes that are invalid. Surely that is something
is should be expected to do?
The current behaviour causes issues for tools that process all classes
in a given directory.


On 08/09/14 11:58, Vicente-Arturo Romero-Zaldivar wrote:
> 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