Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,7 @@ jobs:
uses: actions/setup-java@v1
with:
java-version: 13
- name: Gradle version
run: ./gradlew -version
- name: Build with Gradle
run: ./gradlew build
5 changes: 5 additions & 0 deletions example/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ compileTestJava {

test {
doFirst {
def javaVersion = System.getProperty("java.version").tokenize(".").get(0).toInteger()
if (javaVersion != 13) {
throw new IllegalStateException("Must run build on Java 13 (system reported: ${javaVersion})")
}

def java8home = System.getenv("JAVA_HOME_8")
if (!java8home) {
throw new IllegalStateException("JAVA_HOME_8 must be set")
Expand Down
16 changes: 16 additions & 0 deletions example/src/main/java/com/example/JabelExample.java
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,20 @@ public String innerPublic() {
return outerPrivate();
}
}


// public void whenSwitchingOnOperationSquareMe_thenWillReturnSquare() {
// var me = 4;
// var operation = "squareMe";
// var result = switch (operation) {
// case "doubleMe" -> {
// yield me * 2;
// }
// case "squareMe" -> {
// yield me * me;
// }
// default -> me;
// };
//
// }
}
48 changes: 46 additions & 2 deletions example/src/test/java/com/example/JabelExampleTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,61 @@
public class JabelExampleTest {

@Test(expected = ClassNotFoundException.class)
public void isJava8() throws Exception {
public void isProbablyJava8() throws Exception {
Class.forName("java.lang.StackWalker");
}

@Test
public void shouldWork() {
public void testRuntimeIsJava8() {
String jver = System.getProperty("java.version");
String needle = "1.8";
assertTrue("Java version expected: " + needle + " actual: " + jver, jver.startsWith(needle));
}

@Test
public void testPostJava8Features() {
JabelExample jabelExample = new JabelExample();

String result = jabelExample.run(new String[0]);

assertTrue("'" + result + "' should start with 'idk '", result.startsWith("idk "));
}

@Test
public void testJava13PreviewFeatureTextBlocksJep355(){
String TEXT_BLOCK_JSON = """
{
"name" : "Baeldung",
"website" : "https://www.%s.com/"
}
""";
}

/**
* JEP 354: Switch Expressions (Second Preview)
* https://openjdk.java.net/jeps/354
*/
@Test
public void testJava13PreviewJep354() {
var me = 4;
var operation = "squareMe";
var result = switch (operation) {
case "doubleMe" -> {
yield me * 2;
}
case "squareMe" -> {
yield me * me;
}
default -> me;
};

assertEquals(16, result);
}
//
// @Test
// public void tgesd(){
// JabelExampleTest jabelExampleTest = new JabelExampleTest();
// jabelExampleTest.whenSwitchingOnOperationSquareMe_thenWillReturnSquare();
// }

}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@

public class CheckSourceLevelAdvice {

/**
* If the {@link com.sun.tools.javac.code.Source.Feature} is allowed in JDK8, make javac think it has the same
* characteristics as {@link Source.Feature.LAMBDA}
*
* @param feature
*/
@Advice.OnMethodEnter
public static void checkSourceLevel(
@Advice.Argument(value = 1, readOnly = false) Feature feature
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.github.bsideup.jabel;

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

public class IsPreviewAdvice {

static final boolean debug = false;

/**
* Force javac to think that no features are preview features - on {@link Preview#isPreview} exit, override the
* return value to always be false.
*/
@Advice.OnMethodExit
static public boolean isPreview(Source.Feature feature) {
if (debug) {
/**
* Can't use {@link org.apache.maven.plugin.logging.Log} from instrumented javac classes.
*/
System.out.println("Feature being set to NOT preview: " + feature);
}
return false;
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package com.github.bsideup.jabel;

import com.sun.tools.javac.code.Preview;
import com.sun.tools.javac.code.Source;
import com.sun.tools.javac.parser.JavaTokenizer;
import com.sun.tools.javac.parser.JavacParser;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.agent.ByteBuddyAgent;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.asm.AsmVisitorWrapper.ForDeclaredMethods;
import net.bytebuddy.dynamic.loading.ClassReloadingStrategy;

import javax.annotation.processing.Completion;
Expand Down Expand Up @@ -61,21 +63,29 @@ public class JabelJavacProcessor implements Processor {
.collect(Collectors.toSet());

static {
ByteBuddyAgent.install();
// log that we've started, otherwise if there's certain types of errors, no output is seen at all from Jabel (e.g. errors compiling our code)
// helps for verifying Jabel is being picked up correctly from project settings
logInfo("Jabel static initialising ByteBuddy");

ByteBuddyAgent.install();
ByteBuddy byteBuddy = new ByteBuddy();

for (Class<?> clazz : Arrays.asList(JavacParser.class, JavaTokenizer.class)) {
byteBuddy
.redefine(clazz)
.visit(
Advice.to(CheckSourceLevelAdvice.class)
.on(named("checkSourceLevel").and(takesArguments(2)))
)
.make()
.load(clazz.getClassLoader(), ClassReloadingStrategy.fromInstalledAgent());
}
sourceLevelCheck(byteBuddy);

// previewFeatureChecks(byteBuddy);

featureMinLevelChecks();

logInfo("Jabel ByteBuddy initialisation complete");
}

/**
* For all the features in {@link #ENABLED_FEATURES}, override the min JDK level, reducing it to 8
*
* @see #ENABLED_FEATURES
*/
private static void featureMinLevelChecks() {
logInfo("Disabling feature min level checks");
try {
Field field = Source.Feature.class.getDeclaredField("minLevel");
field.setAccessible(true);
Expand All @@ -91,14 +101,54 @@ public class JabelJavacProcessor implements Processor {
}
}

/**
* Intercept calls to {@link JavacParser#checkSourceLevel}
*
* @see JavacParser#checkSourceLevel
* @see JavaTokenizer#checkSourceLevel
* @see CheckSourceLevelAdvice
*/
private static void sourceLevelCheck(ByteBuddy byteBuddy) {
logInfo("Disabling source level check");
for (Class<?> clazz : Arrays.asList(JavacParser.class, JavaTokenizer.class)) {
ForDeclaredMethods checkSourceLevelMethod = Advice.to(CheckSourceLevelAdvice.class)
.on(named("checkSourceLevel").and(takesArguments(2)));
byteBuddy
.redefine(clazz)
.visit(checkSourceLevelMethod)
.make()
.load(clazz.getClassLoader(), ClassReloadingStrategy.fromInstalledAgent());
}
}

/**
* Intercept call return from {@link Preview#isPreview}
*
* @see Preview#isPreview
* @see IsPreviewAdvice
*/
private static void previewFeatureChecks(ByteBuddy byteBuddy) {
logInfo("Disabling preview feature flag checks");
Class<Preview> previewClass = Preview.class;
ForDeclaredMethods isPreviewMethod = Advice.to(IsPreviewAdvice.class).on(named("isPreview"));
byteBuddy.redefine(previewClass)
.visit(isPreviewMethod)
.make()
.load(previewClass.getClassLoader(), ClassReloadingStrategy.fromInstalledAgent());
}

static private void logInfo(String msg) {
System.out.println(msg);
}

@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.RELEASE_8;
}

@Override
public void init(ProcessingEnvironment processingEnv) {
System.out.println(
logInfo(
ENABLED_FEATURES.stream()
.map(Enum::name)
.collect(Collectors.joining(
Expand Down