Apparent bug in javac's dead code elimination

Jonathan Gibbons jonathan.gibbons at oracle.com
Mon Sep 8 21:17:14 UTC 2014


Chris,

Update:
I see the problem when using JDK 1.7.0.
I do not see the problem using using JDK 1.8.0 or later.

-- Jon

On 09/08/2014 02:01 PM, Jonathan Gibbons wrote:
> Chris,
>
> I agree that javac should only generate valid class files, and that JVMS
> is quite explicit about when a Code attribute is required to be present
> or absent.
>
> I tried some simple experiments of my own and your Cake.java example.
> In neither case do I see any instances of a Code attribute being missing
> when required (or present when not.)  In other words, I have not yet seen
> anything akin to what would give rise to the message you saw with the
> mozilla class:
>
> java.lang.ClassFormatError: Absent Code attribute in method that is not
> native or abstract in class file
>
> Given that it is not an error for javac to be generating an empty 
> class file
> for its own internal purposes, do you have any additional evidence that
> javac is creating an invalid class file?
>
> I was using current builds of JDK 9 for my experiments; what version of
> JDK are you using?
>
> -- Jon
>
> On 09/08/2014 12:27 PM, Chris Kitching wrote:
>> 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