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.
Related
I have observed, that after compiling an interface containing default method definition, like:
interface Delta {
default void someMethod() {
System.out.println("Hi.");
}
}
and after disassembling the respective .class file (including only corresponding snippet here):
javap -v Delta.class
####
{
public void someMethod();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
0: getstatic #1 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #2 // String Hi.
5: invokevirtual #3 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
LineNumberTable:
line 10: 0
line 11: 8
}
default modifier is gone.
Could anyone explain - why?
I am running:
openjdk version "11.0.2" 2019-01-15
OpenJDK Runtime Environment 18.9 (build 11.0.2+9)
OpenJDK 64-Bit Server VM 18.9 (build 11.0.2+9, mixed mode)
Note, that I am aware, that all methods are implicitly public, when no modifier is defined. So, I am not asking why public modifier is present in the compiled version of the file.
Surely looks like a javap bug, see this defect. I've encountered a few more of these javap issues when new features where added, just FYI.
I changed the compilation of a project from Ant to Maven. We still use Java 1.7.0_80. The ByteCode of all classes stayed the same except for a Comparator class. I disassembled the class with javap -c. The only differences are in #-values, e.g.:
before: invokevirtual #2
after: invokevirtual #29
What do these #-values mean? Is this a functional difference or just a difference in naming?
EDIT: The first method in Bytecode before and after:
public de.continentale.kvrl.model.comp.KvRLRechBetragEuroComparator();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public de.continentale.kvrl.model.comp.KvRLRechBetragEuroComparator();
Code:
0: aload_0
1: invokespecial #21 // Method java/lang/Object."<init>":()V
4: return
The #29 refers to an index in the constant table that describes the method being invoked. It would have been better if you had pasted the full output of javap -c, because the comment at the end of the line indicates the actual method descriptor.
Example of javap -c on an invocation of an instance method test in class Z:
7: invokevirtual #19 // Method snippet/Z.test:()V
You can ignore the # number, as long as the method descriptor in the comment is the same.
Why
As to the "why" question, it is very likely caused by the debug in the ant and maven compiler task/plugin settings.
By default, the maven-compiler-plugin compiles with the debug configuration option set to true, which will attach among other the name of the source file, and thing like line number tables and local variable names.
https://maven.apache.org/plugins/maven-compiler-plugin/compile-mojo.html
By default, the ant java compiler has this set to false.
https://ant.apache.org/manual/Tasks/javac.html
To compare and check, set the debug parameter of the Ant Javac task to true and see if that makes the generated .class files the same.
In a static method (Annotated with #CallerSensitive) I try to get the name of the calling class:
#CallerSensitive
public static void someMethod() {
String name = sun.reflect.Reflection.getCallerClass().getName();
...
}
I get the error:
java.lang.InternalError: CallerSensitive annotation expected at frame 1
What is wrong here?
References
http://www.infoq.com/news/2013/07/Oracle-Removes-getCallerClass
http://openjdk.java.net/jeps/176
UPDATE
I am using java 8 (u25) and the method getCallerClass() is not deprecated (getCallerClass(int) is deprecated) as can be seen when disassembling the bytecode:
$ /usr/lib/jvm/java-8-oracle/bin/javap -cp /usr/lib/jvm/java-8-oracle/jre/lib/rt.jar -verbose sun.reflect.Reflection > bytecode
Output (only relevant lines are shown)
Classfile jar:file:/usr/lib/jvm/jdk1.8.0_25/jre/lib/rt.jar!/sun/reflect/Reflection.class
Last modified Sep 17, 2014; size 6476 bytes
Compiled from "Reflection.java"
public class sun.reflect.Reflection
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#78 = Utf8 Lsun/reflect/CallerSensitive;
#80 = Utf8 Deprecated
#82 = Utf8 Ljava/lang/Deprecated;
{
public sun.reflect.Reflection();
descriptor: ()V
flags: ACC_PUBLIC
public static native java.lang.Class<?> getCallerClass();
descriptor: ()Ljava/lang/Class;
flags: ACC_PUBLIC, ACC_STATIC, ACC_NATIVE
Signature: #76 // ()Ljava/lang/Class<*>;
RuntimeVisibleAnnotations:
0: #78()
public static native java.lang.Class<?> getCallerClass(int);
descriptor: (I)Ljava/lang/Class;
flags: ACC_PUBLIC, ACC_STATIC, ACC_NATIVE
Deprecated: true
Signature: #81 // (I)Ljava/lang/Class<*>;
RuntimeVisibleAnnotations:
0: #82()
Only privileged code can use this annotation.
the code is privileged if it is loaded via bootstrap class loader or extension class loader.
excerpt from Open JDK source file classFileParser.cpp
// Privileged code can use all annotations. Other code silently drops some.
const bool privileged = loader_data->is_the_null_class_loader_data() ||
loader_data->is_ext_class_loader_data() ||
loader_data->is_anonymous();
To make java load your classes via bootstrap class loader you can use the -Xbootclasspath/a option to add your classes to the bootstrap class path when running java:
java -Xbootclasspath/a:classes_dir_or_jar_file fully.qualified.mainClassName
To have your classes loaded via extension class loader instead, you need to put your jar file in $JAVA_HOME/jre/lib/ext directory or any other directories set in java.ext.dirs system property.
getCallerClass()is removed from Java8. When I run this example in Java 8, I get the same error. Running with Java 7 (1.7.0_55) I get the name of the calling class. Anyway I would refrain from using anything directly from the sun.* package hierarchy.
To get the name of the calling class you can do the following (I just used the instance initializer to get the name, you should derive from SecurityManager and provide a getCallerClass() method in your class):
public static void someMethod() {
new SecurityManager() {
{
String name = getClassContext()[1].getSimpleName();
System.err.println(name == null ? "null" : name);
}
};
}
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 have been trying to write some Java bytecode and assemble it using Jasmin.
I am trying to get my head around subroutines, and am not sure why I obtain the following error message when running my program:
>java -jar jasmin.jar test.j
Generated: test.class
>java test
Exception in thread "main" java.lang.VerifyError: (class: test,
method: main signature: ([Ljava/lang/String;)V)
Cannot load return address from register 0
Could not find the main class: test. Program will exit.
Here's the bytecode in test.j:
.class public test
.super java/lang/Object
.method public static main([Ljava/lang/String;)V
.limit stack 6
.limit locals 5
jsr a ;Jump to subroutine 'a', pushing return address on operand stack
return ;Exit the program
a:
astore_0 ;Store the return address in variable 0
aload_0 ;Save the address onto the stack as address will be overwritten in 'b'
jsr b ;Jump to subroutine 'b', pushing return address on operand stack
astore_0 ;Store the address that was on the stack back into variable 0
ret 0 ;Return to just after "jsr a"
b:
astore_0 ;Store return address in variable 0
ret 0 ;Return to address stored in 0 (ie back to just after the jump in 'a')
.end method
I haven't had any problems with jumping to a single subroutine, but it seems as though something is going wrong when jumping to a subroutine from within a subroutine.
Any insight as to why this is failing would be much appreciated!
You can't load an address type value into any register, you can only store it and then ret instruction can retrieve it from there.
Java Virtual Machine Specification:
ret
jsr