I was experimenting with enum, and I found that the following compiles and runs fine on Eclipse (Build id: 20090920-1017, not sure exact compiler version):
public class SwitchingOnAnull {
enum X { ,; }
public static void main(String[] args) {
X x = null;
switch(x) {
default: System.out.println("Hello world!");
}
}
}
When compiled and run with Eclipse, this prints "Hello world!" and exits normally.
With the javac compiler, this throws a NullPointerException as expected.
So is there a bug in Eclipse Java compiler?
This is a bug. Here's the specified behavior for a switch statement according to the Java Language Specification, 3rd Edition:
JLS 14.11 The switch Statement
SwitchStatement:
switch ( Expression ) SwitchBlock
When the switch statement is executed, first the Expression is evaluated. If the Expression evaluates to null, a NullPointerException is thrown and the entire switch statement completes abruptly for that reason.
Apparently the bug in Eclipse has nothing to do with default case or enum at all.
public class SwitchingOnAnull {
public static void main(String[] args) {
java.math.RoundingMode x = null;
switch(x) {};
switch((Integer) null) {};
switch((Character) null) {
default: System.out.println("I've got sunshine!");
}
}
}
The above code compiles and runs "fine" on (at least some version of) Eclipse. Each individual switch throws a NullPointerException when compiled with javac, which is exactly as the specification mandates.
The cause
Here's javap -c SwitchingOnAnull when compiled under Eclipse:
Compiled from "SwitchingOnAnull.java"
public class SwitchingOnAnull extends java.lang.Object{
public SwitchingOnAnull();
Code:
0: aload_0
1: invokespecial #8; //Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: aconst_null
1: astore_1
2: getstatic #16; //Field java/lang/System.out:Ljava/io/PrintStream;
5: ldc #22; //String I've got sunshine!
7: invokevirtual #24; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
10: return
}
It seems that the Eclipse compiler gets rid of the entire switch constructs entirely. Unfortunately this optimization breaks the language specification.
The official words
The bug has been filed and assigned for fix.
Olivier Thomann 2010-05-28 08:37:21 EDT
We are too aggressive on the optimization.
For:
switch((Integer) null) {};
we optimize out the whole switch statement when we should at least evaluate the
expression.
I'll take a look.
Candidate for 3.6.1.
See also
Bug 314830 - Switching on a null expression doesn't always throw NullPointerException
Definitly. If we look at the chapter 14.11 of the java language specification, it clearly states (under 'discussion'):
The prohibition against using null as
a switch label prevents one from
writing code that can never be
executed. If the switch expression is
of a reference type, such as a boxed
primitive type or an enum, a run-time
error will occur if the expression
evaluates to null at run-time.
Yep. According to the JLS it's a bug:
If the switch expression is of a reference type, such as a boxed primitive type or an enum, a run-time error will occur if the expression evaluates to null at run-time.
Related
I had a weird bug in an application code, which is an annotation processor and I could find that the root cause of the bug was that the class com.sun.tools.javac.tree.JCTree$JCClassDecl contains the method getSimpleName() twice when I query the class using the reflective method getMethods(). The two versions differ only in the return type. This is legal in JVM code, but not legal in Java. This is not method overloading, because it is only the return type that differs and the return type is not part of the method signature.
The issue can be demonstrated with the simple code:
Method[] methods = com.sun.tools.javac.tree.JCTree.JCClassDecl.class.getMethods();
for (int i = 0; i < methods.length; i++) {
System.out.println(methods[i]);
}
It will print
...
public javax.lang.model.element.Name com.sun.tools.javac.tree.JCTree$JCClassDecl.getSimpleName()
public com.sun.tools.javac.util.Name com.sun.tools.javac.tree.JCTree$JCClassDecl.getSimpleName()
...
(The ellipsis stands for more output lines showing the various other methods that are not interesting for us now.)
The Java version I used to test this is
$ java -version
java version "11" 2018-09-25
Java(TM) SE Runtime Environment 18.9 (build 11+28)
Java HotSpot(TM) 64-Bit Server VM 18.9 (build 11+28, mixed mode)
on a Windows 10 machine.
QUESTION: How was this class code created? My understanding is that this part of the code is written in Java, but in Java this is not possible. Also: what is the aim to have two same-signature versions of a method? Any hint?
If you look at the source code1 you'll see there's only one method with a name of getSimpleName(). This method returns com.sun.tools.javac.util.Name. There's two critical things to note about this:
That method is actually overriding com.sun.source.tree.ClassTree#getSimpleName() which is declared to return javax.lang.model.element.Name.
The com.sun.tools.javac.util.Name abstract class implements the javax.lang.model.element.Name interface, and since the overridden method returns the former it is taking advantage of covariant return types.
According to this Oracle blog, a method which overrides another but declares a covariant return type is implemented using bridge methods.
How is this implemented?
Although the return type based overloading is not allowed by java language, JVM always allowed return type based overloading. JVM uses full signature of a method for lookup/resolution. Full signature includes return type in addition to argument types. i.e., a class can have two or more methods differing only by return type. javac uses this fact to implement covariant return types. In the above, CircleFactory example, javac generates code which is equivalent to the following:
class CircleFactory extends ShapeFactory {
public Circle newShape() {
// your code from the source file
return new Circle();
}
// javac generated method in the .class file
public Shape newShape() {
// call the other newShape method here -- invokevirtual newShape:()LCircle;
}
}
We can use javap with -c option on the class to verify this. Note that we still can't use return type based overloading in source language. But, this is used by javac to support covariant return types. This way, there is no change needed in the JVM to support covariant return types.
And in fact, if you run the following command:
javap -v com.sun.tools.javac.tree.JCTree$JCClassDecl
The following will be output (only including the relevant methods):
public com.sun.tools.javac.util.Name getSimpleName();
descriptor: ()Lcom/sun/tools/javac/util/Name;
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: getfield #13 // Field name:Lcom/sun/tools/javac/util/Name;
4: areturn
LineNumberTable:
line 801: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/sun/tools/javac/tree/JCTree$JCClassDecl;
And:
public javax.lang.model.element.Name getSimpleName();
descriptor: ()Ljavax/lang/model/element/Name;
flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokevirtual #96 // Method getSimpleName:()Lcom/sun/tools/javac/util/Name;
4: areturn
LineNumberTable:
line 752: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/sun/tools/javac/tree/JCTree$JCClassDecl;
As you can see, the second method, the one which returns javax.lang.model.element.Name, is both synthetic and a bridge. In other words, the method is generated by the compiler as part of the implementation of covariant return types. It also simply delegates to the "real" method, the one actually present in the source code which returns com.sun.tools.javac.util.Name.
1. The source code link is for JDK 13.
The official docs just say
14.6. The Empty Statement
An empty statement does nothing.
(https://docs.oracle.com/javase/specs/jls/se7/html/jls-14.html#jls-14.6)
Are statements like ;;;;;;;;;;; actually compiled by the Java compiler?
If so, do these statements take time to be executed, like a nop?
In short: does an empty statement really do "nothing"?
The java compiler does nothing with these statements. Hence, they are ignored.
The following method
public static void test(){
;;;;;;;;
}
just compiles to the following Bytecode:
public static void test();
descriptor: ()V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=0, locals=0, args_size=0
0: return
LineNumberTable:
line 39: 0
Compilers just use NOPs in certain compilation strategies.
Assertions in java compile down to a private synthetic static boolean added to a test - the proposal is nicely documented here:
JSR Assertion Proposal
In it, we create
final private static boolean $assertionsEnabled =
ClassLoader.desiredAssertionStatus(className);
and then assert(X) becomes if ($assertionsEnabled && !x) { throw }
Which makes perfect sense ;)
However, I've noticed that what I actually get is
public void test1(String s) {
assert (!s.equals("Fred"));
System.out.println(s);
}
becomes
static final /* synthetic */ boolean $assertionsDisabled;
public void test1(String s) {
if ((!(AssertTest.$assertionsDisabled)) && (s.equals("Fred"))) {
throw new AssertionError();
}
System.out.println(s);
}
static {
AssertTest.$assertionsDisabled = !(AssertTest.class.desiredAssertionStatus());
}
I can't find any documentation as to why they went with a NEGATIVE test, rather than a positive test - i.e. the original proposal captured assertionsENABLED, now we use assertionsDISABLED.
The only thing I can think of is that this would possibly (POSSIBLY!) generate better branch prediction, but that seems like a pretty lame guess to me - the Java philosophy is (almost) always to make the bytecode simple, and let the JIT sort out optimisations.
( note that this isn't a question about how assertions work - I know that! :) )
( As an aside, it's quite interesting to see that this leads to incorrect tutorials! 6.2.1 of this tutorial, which someone quoted in response to a previous SO question on assertions gets the sense of the test wrong! :)
Any ideas?
This is actually done for a reason, not merely for something like a more compact bytecode or faster condition execution. If you look at the Java Language Specification, §14.10, you will see the following note:
An assert statement that is executed before its class or interface has completed initialization is enabled.
There's also an example that contains an initialization loop:
class Bar {
static {
Baz.testAsserts();
// Will execute before Baz is initialized!
}
}
class Baz extends Bar {
static void testAsserts() {
boolean enabled = false;
assert enabled = true;
System.out.println("Asserts " +
(enabled ? "enabled" : "disabled"));
}
}
As Bar is a superclass for Baz, it must be initialized before Baz initialization. However, its initializer executes an assertion statement in the context of Baz class that is not initialized yet, so there was no chance to set the $assertionsDisabled field. In this case, the field has its default value, and everything works according to the spec: assertion is executed. Had we have an $assertionsEnabled field, the assertions for an uninitialized class would not be executed, so it would violate the specification.
The boolean is actually implemented with an integer. There is a common believe that comparison with zero is quicker, but I don't see any reason to use disable instead of enabled.
IMHO, as false is the default for boolean, I try to chose a flag which has a default value of false In this case $assertionsEnabled would make more sense.
Although it looks like there's redundant work being done when you look at the decompiled java source - you can't rely on this - you need to look at the byte code level.
Have a look at the bytecode both the eclipse compiler and oracle javac produce:
#0: getstatic Test.$assertionsDisabled
#3: ifne #23
(assertion code) #6: aload_1
(assertion code) #7: ldc "Fred"
(assertion code) #9: invokevirtual String.equals(Object)
(assertion code) #12: ifeq #23
(assertion code) #15: new AssertionError
(assertion code) #18: dup
(assertion code) #19: invokespecial AssertionError.<init>()
(assertion code) #22: athrow
#23: getstatic System.out (PrintStream)
#26: aload_1
#27: invokevirtual PrintStream.println(String)
#30: return
Please note byte code #3 - it doesn't need to invert the Test.$assertionsDisabled value, it only needs to perform a single negative test (i.e. it doesn't make any difference if it's a negative test or a positive test at the byte code level)
In summary, it's being implemented efficiently and without any redundant work being performed.
I used MAT tool in Eclipse to investigate a memory leak issue. I found that the leak was caused by an anonymous inner class instance in my app. The class name displayed in MAT is com.mycompany.myproduct.MyActivity$3. There are many anonymous inner classes defined in MyActivity.java. How do I know the which inner class com.mycompany.myproduct.MyActivity$3 indicates?
Thanks.
On the Oracle compiler, they're numbered in order of occurrence in the class. I'm not sure if that's part of any specification or consistent with other implementations.
You could decompile the class--JD-GUI is a great tool for that--and then you'll see what you want to know. You could even just go with basic disassembly using javap -c. It'll give you a rough idea of where the classes occur.
Hint: debugger somehow knows which classes are where. So you can, too!
Try using javap on this example with two anonymous classes:
import java.util.*;
public class Test {
public static void main(String [] args) {
Map m = new HashMap(){{System.out.print(1);}};
Map m1 = new HashMap(){{System.out.print(2);}};
}
}
Compile it and run javap -c -l:
$ javap -c -l Test
Compiled from "Test.java"
public class Test extends java.lang.Object{
public Test();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 3: 0
public static void main(java.lang.String[]);
Code:
0: new #2; //class Test$1
3: dup
4: invokespecial #3; //Method Test$1."<init>":()V
7: astore_1
8: new #4; //class Test$2
11: dup
12: invokespecial #5; //Method Test$2."<init>":()V
15: astore_2
16: return
LineNumberTable:
line 5: 0
line 7: 8
line 9: 16
}
As you can see, the first class got name Test$1, the second one—Test$2. Hope tat helps.
For more specific information, decompile the specific classes you're interested in, e.g. javap -c -l Test\$2. Pay attention to line numbers: they will give you a hint on where in the source file the class was defined.
When you compile your code securely you have MyActivity$1.class, MyActivity$2.class, MyActivity$3.class, and so on. You can use a java decompiler (over your .class) in order to identify the anonymus class that is throwing the exception.
The whole point of anonymous classes is that they are just that. As you have found its not easy to figure out which one its come from. Usually the numbering starts at one, so my guess is that it will be the third declared anonymous class that is your problem.
In this situation, you maybe better off to refactor your code to not have any anonymous classes. Otherwise I suggest attaching the debugger and stepping the code.
I'm translating this code to Clojure. As you can see, I have to extend the class ArthurFrame, yet I'm getting an IllegalAccessError everytime I use (proxy [ArthurFrame] [] ...).
Any idea why? Here is the class's source.
Thanks!
EDIT: Here is the full error stack for running (proxy [ArthurFrame] []) on the REPL.
EDIT 2: Actually it seems that even instantiating the class yields an error. Here is the output from (ArthurFrame. wid):
tried to access class com.trolltech.demos.ArthurFrame from class user$eval__2205
[Thrown class java.lang.IllegalAccessError]
Restarts:
0: [ABORT] Return to SLIME's top level.
Backtrace:
0: user$eval__2205.invoke(NO_SOURCE_FILE:1)
1: clojure.lang.Compiler.eval(Compiler.java:4642)
2: clojure.core$eval__5254.invoke(core.clj:2031)
3: swank.commands.basic$eval_region__907.invoke(basic.clj:40)
4: swank.commands.basic$eval_region__907.invoke(basic.clj:31)
5: swank.commands.basic$eval__927$listener_eval__929.invoke(basic.clj:54)
6: clojure.lang.Var.invoke(Var.java:359)
7: user$eval__2202.invoke(NO_SOURCE_FILE)
8: clojure.lang.Compiler.eval(Compiler.java:4642)
9: clojure.core$eval__5254.invoke(core.clj:2031)
10: swank.core$eval_in_emacs_package__455.invoke(core.clj:59)
11: swank.core$eval_for_emacs__533.invoke(core.clj:128)
12: clojure.lang.Var.invoke(Var.java:367)
13: clojure.lang.AFn.applyToHelper(AFn.java:179)
14: clojure.lang.Var.applyTo(Var.java:476)
15: clojure.core$apply__4379.invoke(core.clj:434)
16: swank.core$eval_from_control__458.invoke(core.clj:66)
17: swank.core$eval_loop__461.invoke(core.clj:71)
18: swank.core$spawn_repl_thread__595$fn__627$fn__629.invoke(core.clj:183)
19: clojure.lang.AFn.applyToHelper(AFn.java:171)
20: clojure.lang.AFn.applyTo(AFn.java:164)
21: clojure.core$apply__4379.invoke(core.clj:434)
22: swank.core$spawn_repl_thread__595$fn__627.doInvoke(core.clj:180)
23: clojure.lang.RestFn.invoke(RestFn.java:402)
24: clojure.lang.AFn.run(AFn.java:37)
25: java.lang.Thread.run(Thread.java:619)
The problem is that ArthurFrame visibility is package not public so the proxy can't access it since the proxy doesn't belong to the package com.trolltech.demos. You have to make ArthurFrame public.
The most likely cause of your problem is that something has not been recompiled. Here's the javadoc description of the IllegalAccessError exception:
Thrown if an application attempts to access or modify a field, or to call a method that it does not have access to.
Normally, this error is caught by the compiler; this error can only occur at run time if the definition of a class has incompatibly changed.
To be more specific, this normally happens when you have classes A and B, where B depends on some members of A. Then you do something like this:
You compile A, then B.
Make an incompatible change to A and recompile it without recompiling B. In this case, the change would involve reducing the visibility of some member of A that B uses so that the member should no longer be visible to B.
Run an application that uses A and B and and you will get an IllegalAccessError.
EDIT
The proxy class that is trying to do the accessing looks like it must have been generated by the Clojure compiler. So maybe there is a Clojure compiler bug ... or maybe you changed the visibility of ArthurFrame after you ran the Clojure compiler. Either way, one possible fix is to change the visibility of the ArthurFrame to public.