28

I'm aware that the runtime features of Java 7 are not available with Java 6 but since no new byte code has been added the new byte code invokedynamic is only relevant for non-Java languages, I was wondering how hard it would be to convert Java 7 source code (new switch statement, diamond operator) to pure Java 6 (i.e. to be able to start to convert the source to Java 7 without losing Java 6 compatibility).

Any pointers?

JasonMArcher
  • 14,195
  • 22
  • 56
  • 52
Aaron Digulla
  • 321,842
  • 108
  • 597
  • 820

4 Answers4

11

As far as I know, there is no solution for this problem at the moment. The best bet would be to extend retrotranslator to deal with Java 1.7 constructs. The diamond operator should be very easy, since it requires no bytecode modification at all.

Your statement "no new byte code has been added" is not correct: There is a new invokedynamic byte code and more importantly there are several cases where the generated bytecode will not be valid for 1.6 JREs, so retrotranslator would have to fix that.

Ingo Kegel
  • 46,523
  • 10
  • 71
  • 102
  • Can you give an example where byte code is generated that's not valid with a Java 6 VM? `invokedynamic` is not used by Java itself, it's only for dynamic languages like Groovy or Jython (and both can live without it). – Aaron Digulla Oct 05 '11 at 15:17
  • 2
    You would have to go through Annex 3 of the Java SE 7 specification (http://download.oracle.com/otndocs/jcp/java_se-7-final-eval-spec/index.html), it contains a PDF with the diffs with respect to Java SE 6. At a cursory glance it seems indeed as if all breaking changes (e.g. new constant pool types) are related to invokedynamic support. – Ingo Kegel Oct 05 '11 at 16:07
  • The problem is that the byte code version number is contained in the class file, so a 1.6 JVM wont load a 1.7 class file. – Angel O'Sphere Oct 07 '11 at 12:19
  • Yes, that's the simplest modification made by retrotranslator. – Ingo Kegel Oct 07 '11 at 12:27
  • How should we convert a switch(string){ case "a": case "b"} ? Do we if-else-if-else all the way? – Pacerier Jan 15 '12 at 13:22
  • 1
    The new switch statement uses the hash code of the strings and then does an equals() in the case. – Aaron Digulla Apr 05 '12 at 14:58
7

Mark a .class file output by Java 7 javac with version 1.6.0 (i.e. 0x32)

printf "\x00\x00\x00\x32" |dd of=Example.class seek=4 bs=1 count=4 conv=notrunc

(according to http://en.wikipedia.org/wiki/Java_class_file#General_layout)

If you put that (using $1 for the filename) into j6patch you can do all class files with:

find . -name \*.class |xargs -I {} ./j6patch {}

I used this on a large (~4.8 MB jar) code base and even used RetroTranslator on the java 6 jar so Java 7 language features can be used on an app that runs in Java 5. Also the Java 7 compiler (javac) does lots of extra optimizations (e.g. escape analysis) that very noticeably improves performance.

Using RetroTranslator with -verify -target 1.5 and JRE 1.6 runtime jars allows to verify that no Java 7 runtime features are used.

Aaron Digulla
  • 321,842
  • 108
  • 597
  • 820
karmakaze
  • 34,689
  • 1
  • 30
  • 32
  • Yes, on a large (~4.8 MB jar) codebase and even used RetroTranslator on the java 6 jar so Java 7 language features can be used on an app that runs in Java 5. Also the Java 7 compiler (javac) does lots of extra opimizations (e.g. escape analysis) that very noticeably improves performance. – karmakaze Aug 22 '13 at 20:42
  • 2
    Additionally you can use RetroTranslator with -verify -target 1.5 and JRE 1.6 runtime jars to verify that no Java 7 runtime is used. – karmakaze Aug 26 '13 at 17:50
  • 1
    Also look at retrolambda in http://stackoverflow.com/questions/23318109/is-it-possible-to-use-java-8-for-android-development Discussion is about Android but also applies to regular Java (with Gradle builds). – karmakaze Dec 20 '15 at 23:59
3

You are right that invokedynamic instruction is not used by Java, however there are some other related changes which can be used in Java. Invokedynamic relies on a new 'Dynamic Linkage Mechanism - Method Handles' for which there are some changes to the invokevirtual instruction as well. You can find more details in this article in the section 'A New Dynamic Linkage Mechanism: Method Handles'.

Method handles also provide a faster alternative to reflection, and hence are useful in Java. Converting code using method handles to Java 6 would not be possible as the feature relies Java 7 VM.

Aaron Digulla
  • 321,842
  • 108
  • 597
  • 820
Deepak Azad
  • 7,903
  • 2
  • 34
  • 49
  • A read of the referenced article seems to indicate that the new 'Dynamic Linkage Mechanism' is supported with not any new bytecode but rather java.dyn (java.lang.invoke.MethodHandle) runtime support. Of course the bytecode should be checked to see this isn't referenced for Java 6 compatibility. – karmakaze Aug 23 '13 at 20:49
2

It's probably some work but try this:

Add Eclipse's Java Compiler to your classpath. It's in the plugin org.eclipse.jdt.core (search for org.eclipse.jdt.core_*.jar in the folder plugins).

This JAR contains the compiler and the parser. You can invoke the parser yourself and then use the ASTVisitor to traverse the parse tree.

You can then modify the tree and create the new source code from this that you can compile as usual.

It might even be possible to "preprocess" the AST tree before the compiler generates byte code; this would save you the "write sources back to disk and compile them from there" step.

Aaron Digulla
  • 321,842
  • 108
  • 597
  • 820