Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix JDK 20-ea build compatibility #3610

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
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import static com.google.errorprone.refaster.Unifier.unifications;

import com.google.auto.value.AutoValue;
import com.google.common.base.VerifyException;
import com.google.errorprone.util.RuntimeVersion;
import com.sun.source.tree.EnhancedForLoopTree;
import com.sun.source.tree.Tree;
Expand All @@ -29,6 +30,7 @@
import com.sun.tools.javac.tree.JCTree.JCStatement;
import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
import com.sun.tools.javac.tree.TreeMaker;
import java.util.Arrays;

/**
* A {@link UTree} representation of a {@link EnhancedForLoopTree}.
Expand All @@ -39,7 +41,63 @@
abstract class UEnhancedForLoop extends USimpleStatement implements EnhancedForLoopTree {
public static UEnhancedForLoop create(
UVariableDecl variable, UExpression elements, UStatement statement) {
return new AutoValue_UEnhancedForLoop(variable, elements, (USimpleStatement) statement);
// On JDK 20 and above the `EnhancedForLoopTree` interface contains a additional method
// `getDeclarationKind()`, referencing a type not available prior to JDK 20. AutoValue
// generates a corresponding field and accessor for this property. Here we find and invoke the
// generated constructor with the appropriate arguments, depending on context.
// See https://github.com/openjdk/jdk20/commit/2cb64a75578ccc15a1dfc8c2843aa11d05ca8aa7.
// TODO: Simplify this logic once JDK 19 and older are no longer supported.
return isCompiledWithJdk20Plus()
? createJdk20PlusEnhancedForLoop(variable, elements, statement)
: createPreJdk20EnhancedForLoop(variable, elements, statement);
Comment on lines +44 to +52
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not happy with all the reflection going on here, but found no better way.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We've mostly been using reflection for similar workarounds recently.

I've been thinking about whether could use Multi-Release jars. At some point it may be worth branches source files like this when there are significant API changes, to avoid the reflection. But at least the last time I looked into it, setting maven up to build mr-jars didn't seem trivial.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using Multi-Release JARs might be a nice idea indeed! I currently don't have any experience with those, so can't comment on the effort required for Maven. (But this does sound like a nice topic to dive into; might also be nice for Error Prone Support, where we're also discussing how to depend on/use newer JDKs while still supporting JDK 11.)

}

private static boolean isCompiledWithJdk20Plus() {
return Arrays.stream(AutoValue_UEnhancedForLoop.class.getDeclaredMethods())
.anyMatch(m -> "getDeclarationKind".equals(m.getName()));
}

private static UEnhancedForLoop createPreJdk20EnhancedForLoop(
UVariableDecl variable, UExpression elements, UStatement statement) {
try {
return AutoValue_UEnhancedForLoop.class
.getDeclaredConstructor(UVariableDecl.class, UExpression.class, USimpleStatement.class)
.newInstance(variable, elements, statement);
} catch (ReflectiveOperationException e) {
throw new LinkageError(e.getMessage(), e);
}
}

private static UEnhancedForLoop createJdk20PlusEnhancedForLoop(
UVariableDecl variable, UExpression elements, UStatement statement) {
Object declarationKind = getVariableDeclarationKind();
try {
return AutoValue_UEnhancedForLoop.class
.getDeclaredConstructor(
declarationKind.getClass(),
UVariableDecl.class,
UExpression.class,
USimpleStatement.class)
.newInstance(declarationKind, variable, elements, statement);
} catch (ReflectiveOperationException e) {
throw new LinkageError(e.getMessage(), e);
}
}

private static Object getVariableDeclarationKind() {
Class<?> declarationKind;
try {
declarationKind = Class.forName("com.sun.source.tree.EnhancedForLoopTree$DeclarationKind");
} catch (ClassNotFoundException e) {
throw new VerifyException("Cannot load `EnhancedForLoopTree.DeclarationKind` enum", e);
}
return Arrays.stream(declarationKind.getEnumConstants())
.filter(v -> "VARIABLE".equals(v.toString()))
.findFirst()
.orElseThrow(
() ->
new VerifyException(
"Enum value `EnhancedForLoopTree.DeclarationKind.VARIABLE` not found"));
}

@Override
Expand Down
1 change: 1 addition & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@
<target>11</target>
<parameters />
<compilerArgs>
<arg>--add-exports=java.base/jdk.internal.javac=ALL-UNNAMED</arg>
<arg>--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED</arg>
<arg>--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED</arg>
<arg>--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED</arg>
Expand Down