Apparent bug in javac's dead code elimination
Chris Kitching
chriskitching at linux.com
Sun Sep 7 03:06:51 UTC 2014
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