Skip to content

Commit

Permalink
Whitelist features for Java 8 instead of hacking Java 12 target (#2)
Browse files Browse the repository at this point in the history
  • Loading branch information
bsideup authored Sep 1, 2019
1 parent 200e4a7 commit 1a10030
Show file tree
Hide file tree
Showing 6 changed files with 160 additions and 171 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,8 @@ build/

.idea/
out/


.project
.classpath
.settings/
38 changes: 26 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Jabel - Javac 12 plugin that makes it emit Java 8 bytecode
# Jabel - use Javac 12+ syntax when targeting Java 8

> Because life is too short to wait for your users to upgrade their Java!
Expand All @@ -14,10 +14,8 @@ But, since most of features after Java 8 did not require a change in the bytecod
## How Jabel works

Although Jabel is an annotation processor, it does not run any processing,
but instruments the java compiler classes and makes it think that Java 12 target
does not support certain bytecode features like indy strings, nestmates, and others.

It also makes it emit Java 8 bytecode major/minor version (instead of Java 12 experimental).
but instruments the java compiler classes and makes it treat some new Java 9+ languages features
as they were supported in Java 8.

The result is a valid Java 8 bytecode for your switch expressions, `var` declarations,
and other features unavaliable in Java 8.
Expand All @@ -31,30 +29,46 @@ First, you need to add Jitpack to your repository list:
repositories {
maven { url 'https://jitpack.io' }
}
```

Then, add Jabel as any other annotation processor:
```groovy
dependencies {
annotationProcessor 'com.github.bsideup.jabel:jabel-javac-plugin:0.1.0'
annotationProcessor 'com.github.bsideup.jabel:jabel-javac-plugin:0.2.0'
}
```

Now, use Java 12 and, if you want to use [Switch expressions](https://openjdk.java.net/jeps/325),
enable preview features:
Now, even if you set source/target/release to 8, the compiler will let you use some new language features.
The full list of features will be printed during the compilation.
```groovy
sourceCompatibility = targetCompatibility = 12
sourceCompatibility = targetCompatibility = 8
compileJava {
options.compilerArgs = [
"--enable-preview"
"--release", "8" // Avoid using Java 12 APIs
]
}
```

That's it! Compile your project and verify that the result is Java 8 bytecode (52.0):
Compile your project and verify that the result is still a valid Java 8 bytecode (52.0):
```shell script
$ ./gradlew --no-daemon clean :example:test

> Task :example:compileJava
Jabel: initialized. Enabled features:
- LOCAL_VARIABLE_TYPE_INFERENCE
- SWITCH_EXPRESSION
- PRIVATE_SAFE_VARARGS
- SWITCH_MULTIPLE_CASE_LABELS
- VAR_SYNTAX_IMPLICIT_LAMBDAS
- DIAMOND_WITH_ANONYMOUS_CLASS_CREATION
- SWITCH_RULE
- EFFECTIVELY_FINAL_VARIABLES_IN_TRY_WITH_RESOURCES


BUILD SUCCESSFUL in 6s
8 actionable tasks: 8 executed

$ javap -v example/build/classes/java/main/com/example/JabelExample.class
Classfile /Users/bsideup/Work/bsideup/jabel/example/build/classes/java/main/com/example/JabelExample.class
Last modified 31 Aug 2019; size 1463 bytes
Expand Down
8 changes: 5 additions & 3 deletions example/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,18 @@ plugins {
id "java"
}

sourceCompatibility = targetCompatibility = 12
sourceCompatibility = targetCompatibility = 8

compileJava {
options.compilerArgs = [
"--enable-preview"
"--release", "8",
]
}

compileTestJava {
sourceCompatibility = targetCompatibility = 8
options.compilerArgs = [
"--release", "8",
]
}

test {
Expand Down
40 changes: 37 additions & 3 deletions example/src/main/java/com/example/JabelExample.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
package com.example;

import java.util.List;
import java.util.concurrent.Callable;
import java.util.function.Function;

public class JabelExample {
public static void main(String[] args) {
System.out.println(new JabelExample().run(args));
}

public String run(String[] args) {
// Switch Expressions
// https://openjdk.java.net/jeps/325
var result = switch (args.length) {
case 1 -> {
break "one";
Expand All @@ -16,9 +22,37 @@ public String run(String[] args) {
return result;
}

private String outerPrivate() {
// Test indy strings
return "idk " + Integer.toString(0);
// Project Coin: Allow @SafeVarargs on private methods
// https://bugs.openjdk.java.net/browse/JDK-7196160
@SafeVarargs
private String outerPrivate(List<String>... args) {
// Look, Ma! No explicit diamond parameter
var callable = new Callable<>() {

@Override
public String call() {
// Var in lambda parameter
Function<String, String> function = (var prefix) -> {
return prefix + Integer.toString(0);
};
// Test indy strings
return function.apply("idk ");
}
};

var closeable = new AutoCloseable() {

@Override
public void close() {

}
};

// Project Coin: Allow final or effectively final variables to be used as resources in try-with-resources
// https://bugs.openjdk.java.net/browse/JDK-7196163
try (closeable) {
return callable.call();
}
}

class Inner {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.github.bsideup.jabel;

import com.sun.tools.javac.code.Source;
import com.sun.tools.javac.code.Source.Feature;
import net.bytebuddy.asm.Advice;

public class CheckSourceLevelAdvice {

@Advice.OnMethodEnter
public static void checkSourceLevelOnExit(
@Advice.Argument(value = 1, readOnly = false) Feature feature
) {
if (feature.allowedInSource(Source.JDK8)) {
//noinspection UnusedAssignment
feature = Source.Feature.LAMBDA;
}
}
}
Loading

0 comments on commit 1a10030

Please sign in to comment.