Apparent bug in javac's dead code elimination
Jonathan Gibbons
jonathan.gibbons at oracle.com
Mon Sep 8 21:01:36 UTC 2014
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