Monday, November 14, 2011

Of Compliance and Source settings in Eclipse java compiler


Going through a few stack overflow posts and a few bugs, I realized that there's still a lot of confusion about the  source and compliance level settings available in JDT. Well, frankly, almost everyone I know was confused when they started using JDT, even the current JDT committers themselves!
These settings are available in Preferences>Java>Compiler, or in your project's .settings folder>org.eclipse.jdt.core.prefs file.



So, what do the settings imply and what do they do?

Source compatibility:
This setting should be used to select upto which java version the user intends to use the language features. Eg.: setting it to 1.7 means that the user can use strings in switch, the diamond construct, etc. but setting it to 1.6 will mean that the user can not use such 1.7-only language features.
For the JDT compiler itself, we use a compliance of 1.4. Yes, quite old-fashioned you'd say, but this was a conscious choice to enable people to use the JDT compiler even if they dont have a version of java higher than 1.4 installed on their machines. You can also chose a source setting for your project on such considerations.

Compiler Compliance level:
This setting is a kind of a switch to make the JDT compiler "compliant" to a certain Oracle javac version. This is because not all bugs fixed in the Oracle javac compiler get backported to earlier releases. So, there are many differences between each javac version. There are even cases when only one javac version differs from the others, before and after it. So, we provided the compliance setting for users to replicate that difference in behaviour in the JDT compiler. This is to enable people who compile with ECJ and ship with javac or vice-versa, to see consistent errors (or the lack thereof).

Take, for example, the following case:

package com.example;

import java.util.List;


public class ErasureBug {


    public String output(List<Integer> integers) {


        return "";
    }


    public void output(List<Double> doubles) {


    }
}


This code compiles without error only in javac 6, but fails with a duplicate method erasure in both javac 5 and javac 7. 


So, in Eclipse when the user sets the project "compliance" to 1.6, he/she will be able to compile the code as shown, while in 1.5 and 1.7 modes, the code will fail.




Generated .class files compatibility:
This setting changes the generated bytecode to be compatible to a certain VM version. This is required at runtime when the VM available is lower than the one for which the code is compiled. So, if you set source level at 1.3 , use 1.3 features your code and expect your clients to still be able to use the older 1.2 or lesser VM to execute your code, you should set the .class file compatibility setting to 1.2 or less. This does not work for higher java versions, because suppose you use annotations in your code which only came in 1.5, or instructions such as invokedynamic in 1.7, which introduced a new Bootstrap method attribute, earlier VM's may still not be able to execute the class files. So we cannot guarantee backward compatibility and the UI thus will not allow you to set class file compatibility lower than the source level. However, if you're using features such as generics in 1.5 source level, it's quite safe for a 1.4 VM to execute those. So, you can manually set the org.eclipse.jdt.core.compiler.codegen.targetPlatform property to 1.4 in project>settings folder>org.eclipse.jdt.core.prefs file.


Note that the source level cannot be more than the compliance level.


Hope this gives more clarity on the settings.


EclipseCon update:
I have the following submissions for EclipseCon 2012. Do vote if you find them interesting: