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

Add lombok support for java-11 #4769

Merged
merged 27 commits into from
Dec 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
481ef95
Add lombok support for java-11
amishra-u Dec 11, 2024
533a3e0
Merge branch 'openrewrite:main' into lombok
amishra-u Dec 19, 2024
7b51a4b
Handle erroneous nodes in open rewrite (#4412)
vudayani Dec 19, 2024
91d6eff
Make Groovy Parser correctly handle nested parenthesis (#4801)
Laurens-W Dec 19, 2024
2a5080f
suppress javax.json (#4804)
pstreef Dec 20, 2024
e5d3fce
Refactor SpringReference (#4805)
Laurens-W Dec 20, 2024
152772c
refactor: Update Gradle wrapper (#4808)
shanman190 Dec 20, 2024
85db15f
Add recipe to remove Gradle Enterprise and Develocity (#4809)
timtebeek Dec 21, 2024
ef9c7d5
Add a UsesType precondition to ReplaceConstant
timtebeek Dec 21, 2024
c4cdbcb
Allow file scheme in `RemoteArchive` to simplify testing (#4791)
pstreef Dec 23, 2024
435311f
Try alternative way of determining parenthesis level for `BinaryExpre…
Laurens-W Dec 23, 2024
043734e
Add a `isClassAvailable` method to the ReflectionUtils (#4810)
jevanlingen Dec 24, 2024
ee5e4ae
Update rewrite.yml to enforce CompareEnumsWithEqualityOperator
timtebeek Dec 24, 2024
d004592
Correctly map generic return and parameter types in `JavaReflectionTy…
jkschneider Dec 24, 2024
a1963a9
Polish formatting
jkschneider Dec 24, 2024
443cee9
Add more scenarios to JavaTypeGoat for simply typed fields and method…
jkschneider Dec 24, 2024
5b8ac64
Support mapping of generic thrown exception types (#4813)
jkschneider Dec 25, 2024
d6790ce
refactor: Enum values should be compared with "==" (#4811)
timtebeek Dec 25, 2024
ef1e3b6
Keep the names of generic type variables defined by methods. (#4814)
jkschneider Dec 25, 2024
13cd88c
Fix Java reflection mapping of generic typed fields. (#4815)
jkschneider Dec 25, 2024
fb008d8
Revert parenthesis changes (#4818)
timtebeek Dec 27, 2024
d265c7b
JavaTemplate bug when inserting `final var` into for-each (#4806)
timtebeek Dec 27, 2024
edcdc3a
Merge branch 'openrewrite:main' into lombok
amishra-u Dec 28, 2024
3263945
Merge branch 'main' into lombok
amishra-u Dec 30, 2024
f0d92a5
Merge branch 'main' into lombok
timtebeek Dec 31, 2024
b3296e2
Reduce accidental changes between Java 11 and 17 parsers
timtebeek Dec 31, 2024
d4290e6
Add missing import
timtebeek Dec 31, 2024
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
1 change: 1 addition & 0 deletions rewrite-java-11/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ val javaTck = configurations.create("javaTck") {
dependencies {
api(project(":rewrite-core"))
api(project(":rewrite-java"))
runtimeOnly(project(":rewrite-java-lombok"))

compileOnly("org.slf4j:slf4j-api:1.7.+")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@
import com.sun.tools.javac.comp.*;
import com.sun.tools.javac.file.JavacFileManager;
import com.sun.tools.javac.main.JavaCompiler;
import com.sun.tools.javac.main.Option;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.Log;
import com.sun.tools.javac.util.Options;
import io.micrometer.core.instrument.Metrics;
import io.micrometer.core.instrument.Timer;
import lombok.Getter;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
import org.objectweb.asm.ClassReader;
Expand All @@ -43,20 +45,28 @@
import org.openrewrite.tree.ParseError;
import org.openrewrite.tree.ParsingEventListener;
import org.openrewrite.tree.ParsingExecutionContextView;
import org.slf4j.LoggerFactory;

import javax.annotation.processing.Processor;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import javax.tools.StandardLocation;
import java.io.*;
import java.lang.reflect.Constructor;
import java.net.URI;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import static java.util.stream.Collectors.toList;

/**
Expand All @@ -77,14 +87,16 @@ public class ReloadableJava11Parser implements JavaParser {
private final JavaCompiler compiler;
private final ResettableLog compilerLog;
private final Collection<NamedStyles> styles;

private ReloadableJava11Parser(boolean logCompilationWarningsAndErrors,
@Nullable Collection<Path> classpath,
Collection<byte[]> classBytesClasspath,
@Nullable Collection<Input> dependsOn,
Charset charset,
Collection<NamedStyles> styles,
JavaTypeCache typeCache) {
private final List<Processor> annotationProcessors;

private ReloadableJava11Parser(
boolean logCompilationWarningsAndErrors,
@Nullable Collection<Path> classpath,
Collection<byte[]> classBytesClasspath,
@Nullable Collection<Input> dependsOn,
Charset charset,
Collection<NamedStyles> styles,
JavaTypeCache typeCache) {
this.classpath = classpath;
this.dependsOn = dependsOn;
this.styles = styles;
Expand All @@ -106,6 +118,65 @@ private ReloadableJava11Parser(boolean logCompilationWarningsAndErrors,
Options.instance(context).put("-g", "-g");
Options.instance(context).put("-proc", "none");

LOMBOK:
if (System.getenv().getOrDefault("REWRITE_LOMBOK", System.getProperty("rewrite.lombok")) != null &&
classpath != null && classpath.stream().anyMatch(it -> it.toString().contains("lombok"))) {
Processor lombokProcessor = null;
try {
// https://projectlombok.org/contributing/lombok-execution-path
List<String> overrideClasspath = new ArrayList<>();
for (Path part : classpath) {
if (part.toString().contains("lombok")) {
overrideClasspath.add(part.toString());
}
}
// make sure the rewrite-java-lombok dependency comes first
boolean found = false;
for (int i = 0; i < overrideClasspath.size(); i++) {
if (overrideClasspath.get(i).contains("rewrite-java-lombok")) {
overrideClasspath.add(0, overrideClasspath.remove(i));
found = true;
}
}
if (!found) {
// try to find `rewrite-java-lombok` using class loader
URL resource = getClass().getClassLoader().getResource("org/openrewrite/java/lombok/OpenRewriteConfigurationKeysLoader.class");
if (resource != null && resource.getProtocol().equals("jar") && resource.getPath().startsWith("file:")) {
String path = Paths.get(URI.create(resource.getPath().substring(0, resource.getPath().indexOf("!")))).toString();
overrideClasspath.add(0, path);
} else {
break LOMBOK;
}
}
System.setProperty("shadow.override.lombok", String.join(File.pathSeparator, overrideClasspath));

Class<?> shadowLoaderClass = Class.forName("lombok.launch.ShadowClassLoader", true, getClass().getClassLoader());
Constructor<?> shadowLoaderConstructor = shadowLoaderClass.getDeclaredConstructor(
Class.forName("java.lang.ClassLoader"),
Class.forName("java.lang.String"),
Class.forName("java.lang.String"),
Class.forName("java.util.List"),
Class.forName("java.util.List"));
shadowLoaderConstructor.setAccessible(true);

ClassLoader lombokShadowLoader = (ClassLoader) shadowLoaderConstructor.newInstance(
getClass().getClassLoader(),
"lombok",
null,
emptyList(),
singletonList("lombok.patcher.Symbols")
);
lombokProcessor = (Processor) lombokShadowLoader.loadClass("lombok.core.AnnotationProcessor").getDeclaredConstructor().newInstance();
Options.instance(context).put(Option.PROCESSOR, "lombok.launch.AnnotationProcessorHider$AnnotationProcessor");
} catch (ReflectiveOperationException ignore) {
// Lombok was not found or could not be initialized
} finally {
annotationProcessors = lombokProcessor != null ? singletonList(lombokProcessor) : emptyList();
}
} else {
annotationProcessors = emptyList();
}

// MUST be created ahead of compiler construction
new TimedTodo(context);

Expand All @@ -127,7 +198,7 @@ public void write(char[] cbuf, int off, int len) {
if (logCompilationWarningsAndErrors) {
String log = new String(Arrays.copyOfRange(cbuf, off, len));
if (!log.isBlank()) {
org.slf4j.LoggerFactory.getLogger(ReloadableJava11Parser.class).warn(log);
LoggerFactory.getLogger(ReloadableJava11Parser.class).warn(log);
}
}
}
Expand Down Expand Up @@ -191,37 +262,44 @@ LinkedHashMap<Input, JCTree.JCCompilationUnit> parseInputsToCompilerAst(Iterable
}

LinkedHashMap<Input, JCTree.JCCompilationUnit> cus = new LinkedHashMap<>();
acceptedInputs(sourceFiles).forEach(input1 -> {
List<ReloadableJava11ParserInputFileObject> inputFileObjects = acceptedInputs(sourceFiles)
.map(input -> new ReloadableJava11ParserInputFileObject(input, ctx))
.collect(Collectors.toList());
if (!annotationProcessors.isEmpty()) {
compiler.initProcessAnnotations(annotationProcessors, inputFileObjects, emptyList());
}
try {
//noinspection unchecked
com.sun.tools.javac.util.List<JCTree.JCCompilationUnit> jcCompilationUnits = compiler.parseFiles((List<JavaFileObject>) (List<?>) inputFileObjects);
for (int i = 0; i < inputFileObjects.size(); i++) {
cus.put(inputFileObjects.get(i).getInput(), jcCompilationUnits.get(i));
}
try {
JCTree.JCCompilationUnit jcCompilationUnit = compiler.parse(new ReloadableJava11ParserInputFileObject(input1, ctx));
cus.put(input1, jcCompilationUnit);
} catch (IllegalStateException e) {
if ("endPosTable already set".equals(e.getMessage())) {
throw new IllegalStateException(
"Call reset() on JavaParser before parsing another set of source files that " +
"have some of the same fully qualified names. Source file [" +
input1.getPath() + "]\n[\n" + StringUtils.readFully(input1.getSource(ctx), getCharset(ctx)) + "\n]", e);
initModules(cus.values());
enterAll(cus.values());

// For some reason this is necessary in JDK 9+, where the internal block counter that
// annotationsBlocked() tests against remains >0 after attribution.
Annotate annotate = Annotate.instance(context);
while (annotate.annotationsBlocked()) {
annotate.unblockAnnotations(); // also flushes once unblocked
}
if (!annotationProcessors.isEmpty()) {
compiler.processAnnotations(jcCompilationUnits, emptyList());
}
throw e;
compiler.attribute(compiler.todo);
} catch (Throwable t) {
// when symbol entering fails on problems like missing types, attribution can often times proceed
// unhindered, but it sometimes cannot (so attribution is always best-effort in the presence of errors)
ctx.getOnError().accept(new JavaParsingException("Failed symbol entering or attribution", t));
}
});

try {
initModules(cus.values());
enterAll(cus.values());

// For some reason this is necessary in JDK 9+, where the internal block counter that
// annotationsBlocked() tests against remains >0 after attribution.
Annotate annotate = Annotate.instance(context);
while (annotate.annotationsBlocked()) {
annotate.unblockAnnotations(); // also flushes once unblocked
} catch (IllegalStateException e) {
if ("endPosTable already set".equals(e.getMessage())) {
throw new IllegalStateException(
"Call reset() on JavaParser before parsing another set of source files that " +
"have some of the same fully qualified names.", e);
}

compiler.attribute(compiler.todo);
} catch (Throwable t) {
// when symbol entering fails on problems like missing types, attribution can often times proceed
// unhindered, but it sometimes cannot (so attribution is always a BEST EFFORT in the presence of errors)
ctx.getOnError().accept(new JavaParsingException("Failed symbol entering or attribution", t));
throw e;
}
return cus;
}
Expand Down Expand Up @@ -361,8 +439,7 @@ public String inferBinaryName(Location location, JavaFileObject file) {
public Iterable<JavaFileObject> list(Location location, String packageName, Set<JavaFileObject.Kind> kinds, boolean recurse) throws IOException {
if (StandardLocation.CLASS_PATH.equals(location)) {
Iterable<JavaFileObject> listed = super.list(location, packageName, kinds, recurse);
return Stream.concat(
classByteClasspath.stream()
return Stream.concat(classByteClasspath.stream()
.filter(jfo -> jfo.getPackage().equals(packageName)),
StreamSupport.stream(listed.spliterator(), false)
).collect(toList());
Expand All @@ -373,6 +450,7 @@ public Iterable<JavaFileObject> list(Location location, String packageName, Set<

private static class PackageAwareJavaFileObject extends SimpleJavaFileObject {
private final String pkg;
@Getter
private final String className;
private final byte[] classBytes;

Expand Down Expand Up @@ -406,10 +484,6 @@ public String getPackage() {
return pkg;
}

public String getClassName() {
return className;
}

@Override
public InputStream openInputStream() {
return new ByteArrayInputStream(classBytes);
Expand Down
Loading
Loading