diff --git a/build.gradle b/build.gradle index de14ed9..c72e8a6 100644 --- a/build.gradle +++ b/build.gradle @@ -76,19 +76,20 @@ def launcher8 = javaToolchains.launcherFor { languageVersion.set(JavaLanguageVer def javaHome8 = launcher8.map { it.metadata.installationPath.asFile } def jvm8 = Jvm.forHome(javaHome8.get()) +apply from: "$rootDir/gradle/java8-compile.gradle" +apply from: "$rootDir/gradle/multi-release.gradle" +apply from: "$rootDir/gradle/publishing.gradle" +apply from: "$rootDir/gradle/test.gradle" + dependencies { implementation files(jvm8.toolsJar) api project(":dd-javac-plugin-client") - api "org.burningwave:core:12.62.7" + + java9Implementation "org.burningwave:core:12.65.1" testImplementation "org.junit.jupiter:junit-jupiter-api:5.9.2" - testImplementation 'org.junit.jupiter:junit-jupiter-params:5.9.2' - testImplementation 'commons-io:commons-io:2.11.0' + testImplementation "org.junit.jupiter:junit-jupiter-params:5.9.2" + testImplementation "commons-io:commons-io:2.11.0" testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:5.9.2" } - -apply from: "$rootDir/gradle/java8-compile.gradle" -apply from: "$rootDir/gradle/multi-release.gradle" -apply from: "$rootDir/gradle/publishing.gradle" -apply from: "$rootDir/gradle/test.gradle" diff --git a/gradle/multi-release.gradle b/gradle/multi-release.gradle index 5d3d726..2881b21 100644 --- a/gradle/multi-release.gradle +++ b/gradle/multi-release.gradle @@ -3,6 +3,7 @@ sourceSets { java { srcDir 'src/main/java9' } + compileClasspath += sourceSets.main.output } } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 070cb70..0d18421 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/src/main/java/datadog/compiler/AnnotationsInjectingClassVisitor.java b/src/main/java/datadog/compiler/AnnotationsInjectingClassVisitor.java new file mode 100644 index 0000000..84c568a --- /dev/null +++ b/src/main/java/datadog/compiler/AnnotationsInjectingClassVisitor.java @@ -0,0 +1,113 @@ +package datadog.compiler; + +import com.sun.source.tree.BlockTree; +import com.sun.source.tree.ClassTree; +import com.sun.source.tree.LineMap; +import com.sun.source.tree.MethodTree; +import com.sun.source.util.TreeScanner; +import com.sun.tools.javac.code.Flags; +import com.sun.tools.javac.tree.EndPosTable; +import com.sun.tools.javac.tree.JCTree; +import com.sun.tools.javac.tree.TreeMaker; +import com.sun.tools.javac.util.List; +import com.sun.tools.javac.util.Name; +import com.sun.tools.javac.util.Names; +import com.sun.tools.javac.util.Position; + +public class AnnotationsInjectingClassVisitor extends TreeScanner { + private final TreeMaker maker; + private final Names names; + private final JCTree.JCAnnotation sourcePathAnnotation; + private final JCTree.JCExpression methodLinesAnnotationType; + private final boolean methodAnnotationDisabled; + private final LineMap lineMap; + private final EndPosTable endPositions; + + AnnotationsInjectingClassVisitor(TreeMaker maker, + Names names, + JCTree.JCAnnotation sourcePathAnnotation, + JCTree.JCExpression methodLinesAnnotationType, + boolean methodAnnotationDisabled, + LineMap lineMap, + EndPosTable endPositions) { + this.maker = maker; + this.names = names; + this.sourcePathAnnotation = sourcePathAnnotation; + this.methodLinesAnnotationType = methodLinesAnnotationType; + this.methodAnnotationDisabled = methodAnnotationDisabled; + this.lineMap = lineMap; + this.endPositions = endPositions; + } + + @Override + public Void visitClass(ClassTree node, Void aVoid) { + JCTree.JCClassDecl classDeclaration = (JCTree.JCClassDecl) node; + for (JCTree.JCAnnotation annotation : classDeclaration.mods.annotations) { + if (annotation.annotationType.toString().endsWith("SourcePath")) { + // The method is already annotated with @SourcePath. + // This can happen, for instance, when code-generation tools are used + // that copy annotations from interface to class + return super.visitClass(node, aVoid); + } + } + + if (node.getSimpleName().length() > 0) { + classDeclaration.mods.annotations = classDeclaration.mods.annotations.prepend(sourcePathAnnotation); + } + return super.visitClass(node, aVoid); + } + + public Void visitMethod(MethodTree node, Void aVoid) { + if (!methodAnnotationDisabled && (node instanceof JCTree.JCMethodDecl)) { + JCTree.JCMethodDecl methodDecl = (JCTree.JCMethodDecl) node; + + for (JCTree.JCAnnotation annotation : methodDecl.mods.annotations) { + if (annotation.annotationType.toString().endsWith("MethodLines")) { + // The method is already annotated with @MethodLines. + // This can happen, for instance, when code-generation tools are used + // that copy annotations from interface methods to class methods + return super.visitMethod(node, aVoid); + } + } + + JCTree.JCModifiers modifiers = methodDecl.getModifiers(); + if ((modifiers.flags & Flags.PUBLIC) != 0) { + + int startPosition = modifiers.getStartPosition(); + if (startPosition == Position.NOPOS) { + startPosition = methodDecl.getStartPosition(); + } + + int endPosition = methodDecl.getEndPosition(endPositions); + if (endPosition == Position.NOPOS) { + BlockTree methodBody = node.getBody(); + if (methodBody != null) { + JCTree methodBodyTree = (JCTree) methodBody; + endPosition = methodBodyTree.getEndPosition(endPositions); + } + } + + int startLine = (int) lineMap.getLineNumber(startPosition); + int endLine = (int) lineMap.getLineNumber(endPosition); + + Name startName = names.fromString("start"); + JCTree.JCIdent startIdent = maker.Ident(startName); + JCTree.JCLiteral startLiteral = maker.Literal(startLine); + JCTree.JCAssign startAssign = maker.Assign(startIdent, startLiteral); + + Name endName = names.fromString("end"); + JCTree.JCIdent endIdent = maker.Ident(endName); + JCTree.JCLiteral endLiteral = maker.Literal(endLine); + JCTree.JCAssign endAssign = maker.Assign(endIdent, endLiteral); + + JCTree.JCAnnotation methodLinesAnnotation = annotation(maker, methodLinesAnnotationType, startAssign, endAssign); + methodDecl.mods.annotations = methodDecl.mods.annotations.prepend(methodLinesAnnotation); + } + } + return super.visitMethod(node, aVoid); + } + + private static JCTree.JCAnnotation annotation(TreeMaker maker, JCTree type, JCTree.JCExpression... arguments) { + return maker.Annotation(type, List.from(arguments)); + } +} diff --git a/src/main/java/datadog/compiler/CompilerModuleOpener.java b/src/main/java/datadog/compiler/CompilerModuleOpener.java new file mode 100644 index 0000000..a2b612e --- /dev/null +++ b/src/main/java/datadog/compiler/CompilerModuleOpener.java @@ -0,0 +1,9 @@ +package datadog.compiler; + +public class CompilerModuleOpener { + + public static void setup() { + // this is a stub used only for JDK 8. It does nothing. See corresponding class in JDK 9+ sources. + } + +} \ No newline at end of file diff --git a/src/main/java/datadog/compiler/DatadogCompilerPlugin.java b/src/main/java/datadog/compiler/DatadogCompilerPlugin.java index 85a3443..6bd1259 100644 --- a/src/main/java/datadog/compiler/DatadogCompilerPlugin.java +++ b/src/main/java/datadog/compiler/DatadogCompilerPlugin.java @@ -1,54 +1,19 @@ package datadog.compiler; -import com.sun.source.tree.BlockTree; -import com.sun.source.tree.ClassTree; -import com.sun.source.tree.CompilationUnitTree; -import com.sun.source.tree.LineMap; -import com.sun.source.tree.MethodTree; import com.sun.source.util.JavacTask; import com.sun.source.util.Plugin; -import com.sun.source.util.TaskEvent; -import com.sun.source.util.TaskListener; -import com.sun.source.util.TreeScanner; import com.sun.tools.javac.api.BasicJavacTask; -import com.sun.tools.javac.code.Flags; -import com.sun.tools.javac.tree.EndPosTable; -import com.sun.tools.javac.tree.JCTree; -import com.sun.tools.javac.tree.TreeMaker; import com.sun.tools.javac.util.Context; -import com.sun.tools.javac.util.List; import com.sun.tools.javac.util.Log; -import com.sun.tools.javac.util.Name; -import com.sun.tools.javac.util.Names; -import com.sun.tools.javac.util.Position; -import java.io.PrintWriter; -import java.net.URI; -import java.nio.file.Path; -import java.nio.file.Paths; import java.util.Arrays; import java.util.Collection; -import javax.tools.JavaFileObject; -import org.burningwave.core.assembler.StaticComponentContainer; -import org.burningwave.core.function.Executor; -import org.burningwave.core.function.ThrowingRunnable; public class DatadogCompilerPlugin implements Plugin { - static final String DISABLE_UNNAMED_MODULE_OPEN = "disableUnnamedModuleOpen"; static final String DISABLE_METHOD_ANNOTATION = "disableMethodAnnotation"; static { - try { - // to free users from having to declare --add-exports: https://openjdk.org/jeps/396 - if (StaticComponentContainer.JVMInfo.getVersion() >= 16) { - StaticComponentContainer.Modules.exportToAllUnnamed("jdk.compiler"); - } - // force classes to be loaded: https://github.com/burningwave/core/discussions/15 - ThrowingRunnable.class.getClassLoader(); - Executor.class.getClassLoader(); - } catch (Throwable e) { - // ignore - } + CompilerModuleOpener.setup(); } static final String NAME = "DatadogCompilerPlugin"; @@ -65,182 +30,10 @@ public void init(JavacTask task, String... strings) { Context context = basicJavacTask.getContext(); Collection arguments = Arrays.asList(strings); - boolean unnamedModuleOpenDisabled = arguments.contains(DISABLE_UNNAMED_MODULE_OPEN); - if (!unnamedModuleOpenDisabled) { - UnnamedModuleOpener.open(context); - } - boolean methodAnnotationDisabled = arguments.contains(DISABLE_METHOD_ANNOTATION); - task.addTaskListener(new DatadogCompilerPluginTaskListener(basicJavacTask, methodAnnotationDisabled)); - Log.instance(context).printRawLines(Log.WriterKind.NOTICE, NAME + " initialized"); - } - } - - private static final class DatadogCompilerPluginTaskListener implements TaskListener { - private final BasicJavacTask basicJavacTask; - private final boolean methodAnnotationDisabled; - - private DatadogCompilerPluginTaskListener(BasicJavacTask basicJavacTask, boolean methodAnnotationDisabled) { - this.basicJavacTask = basicJavacTask; - this.methodAnnotationDisabled = methodAnnotationDisabled; - } - - @Override - public void started(TaskEvent e) { - } - - @Override - public void finished(TaskEvent e) { - if (e.getKind() != TaskEvent.Kind.PARSE) { - return; - } - - Context context = basicJavacTask.getContext(); - try { - JavaFileObject sourceFile = e.getSourceFile(); - URI sourceUri = sourceFile.toUri(); - Path sourcePath = Paths.get(sourceUri).toAbsolutePath(); - - TreeMaker maker = TreeMaker.instance(context); - Names names = Names.instance(context); - - JCTree.JCExpression sourcePathAnnotationType = select(maker, names, "datadog", "compiler", "annotations", "SourcePath"); - JCTree.JCAnnotation sourcePathAnnotation = annotation(maker, sourcePathAnnotationType, maker.Literal(sourcePath.toString())); - - JCTree.JCExpression methodLinesAnnotationType = select(maker, names, "datadog", "compiler", "annotations", "MethodLines"); - - CompilationUnitTree compilationUnit = e.getCompilationUnit(); - LineMap lineMap = compilationUnit.getLineMap(); - EndPosTable endPositions; - if (compilationUnit instanceof JCTree.JCCompilationUnit) { - JCTree.JCCompilationUnit jcCompilationUnit = (JCTree.JCCompilationUnit) compilationUnit; - endPositions = jcCompilationUnit.endPositions; - } else { - endPositions = null; - } - - SourcePathInjectingClassVisitor treeVisitor = new SourcePathInjectingClassVisitor( - maker, names, sourcePathAnnotation, methodLinesAnnotationType, methodAnnotationDisabled, lineMap, endPositions); - compilationUnit.accept(treeVisitor, null); - - } catch (Throwable t) { - Log log = Log.instance(context); - log.printRawLines( - Log.WriterKind.WARNING, - "Could not inject source information into " - + log.currentSourceFile().toUri() - + ": " - + t.getMessage()); - - PrintWriter logWriter = log.getWriter(Log.WriterKind.WARNING); - t.printStackTrace(logWriter); - } - } - } - - private static JCTree.JCExpression select(TreeMaker maker, Names names, String... parts) { - JCTree.JCExpression id = maker.Ident(names.fromString(parts[0])); - for (int i = 1; i < parts.length; i++) { - id = maker.Select(id, names.fromString(parts[i])); - } - return id; - } - - private static JCTree.JCAnnotation annotation(TreeMaker maker, JCTree type, JCTree.JCExpression... arguments) { - return maker.Annotation(type, List.from(arguments)); - } + task.addTaskListener(new DatadogTaskListener(basicJavacTask, methodAnnotationDisabled)); - private static final class SourcePathInjectingClassVisitor extends TreeScanner { - private final TreeMaker maker; - private final Names names; - private final JCTree.JCAnnotation sourcePathAnnotation; - private final JCTree.JCExpression methodLinesAnnotationType; - private final boolean methodAnnotationDisabled; - private final LineMap lineMap; - private final EndPosTable endPositions; - - private SourcePathInjectingClassVisitor(TreeMaker maker, - Names names, - JCTree.JCAnnotation sourcePathAnnotation, - JCTree.JCExpression methodLinesAnnotationType, - boolean methodAnnotationDisabled, - LineMap lineMap, - EndPosTable endPositions) { - this.maker = maker; - this.names = names; - this.sourcePathAnnotation = sourcePathAnnotation; - this.methodLinesAnnotationType = methodLinesAnnotationType; - this.methodAnnotationDisabled = methodAnnotationDisabled; - this.lineMap = lineMap; - this.endPositions = endPositions; - } - - @Override - public Void visitClass(ClassTree node, Void aVoid) { - JCTree.JCClassDecl classDeclaration = (JCTree.JCClassDecl) node; - for (JCTree.JCAnnotation annotation : classDeclaration.mods.annotations) { - if (annotation.annotationType.toString().endsWith("SourcePath")) { - // The method is already annotated with @SourcePath. - // This can happen, for instance, when code-generation tools are used - // that copy annotations from interface to class - return super.visitClass(node, aVoid); - } - } - - if (node.getSimpleName().length() > 0) { - classDeclaration.mods.annotations = classDeclaration.mods.annotations.prepend(sourcePathAnnotation); - } - return super.visitClass(node, aVoid); - } - - public Void visitMethod(MethodTree node, Void aVoid) { - if (!methodAnnotationDisabled && (node instanceof JCTree.JCMethodDecl)) { - JCTree.JCMethodDecl methodDecl = (JCTree.JCMethodDecl) node; - - for (JCTree.JCAnnotation annotation : methodDecl.mods.annotations) { - if (annotation.annotationType.toString().endsWith("MethodLines")) { - // The method is already annotated with @MethodLines. - // This can happen, for instance, when code-generation tools are used - // that copy annotations from interface methods to class methods - return super.visitMethod(node, aVoid); - } - } - - JCTree.JCModifiers modifiers = methodDecl.getModifiers(); - if ((modifiers.flags & Flags.PUBLIC) != 0) { - - int startPosition = modifiers.getStartPosition(); - if (startPosition == Position.NOPOS) { - startPosition = methodDecl.getStartPosition(); - } - - int endPosition = methodDecl.getEndPosition(endPositions); - if (endPosition == Position.NOPOS) { - BlockTree methodBody = node.getBody(); - if (methodBody != null) { - JCTree methodBodyTree = (JCTree) methodBody; - endPosition = methodBodyTree.getEndPosition(endPositions); - } - } - - int startLine = (int) lineMap.getLineNumber(startPosition); - int endLine = (int) lineMap.getLineNumber(endPosition); - - Name startName = names.fromString("start"); - JCTree.JCIdent startIdent = maker.Ident(startName); - JCTree.JCLiteral startLiteral = maker.Literal(startLine); - JCTree.JCAssign startAssign = maker.Assign(startIdent, startLiteral); - - Name endName = names.fromString("end"); - JCTree.JCIdent endIdent = maker.Ident(endName); - JCTree.JCLiteral endLiteral = maker.Literal(endLine); - JCTree.JCAssign endAssign = maker.Assign(endIdent, endLiteral); - - JCTree.JCAnnotation methodLinesAnnotation = annotation(maker, methodLinesAnnotationType, startAssign, endAssign); - methodDecl.mods.annotations = methodDecl.mods.annotations.prepend(methodLinesAnnotation); - } - } - return super.visitMethod(node, aVoid); + Log.instance(context).printRawLines(Log.WriterKind.NOTICE, NAME + " initialized"); } } diff --git a/src/main/java/datadog/compiler/DatadogTaskListener.java b/src/main/java/datadog/compiler/DatadogTaskListener.java new file mode 100644 index 0000000..2fa1788 --- /dev/null +++ b/src/main/java/datadog/compiler/DatadogTaskListener.java @@ -0,0 +1,104 @@ +package datadog.compiler; + +import com.sun.source.tree.CompilationUnitTree; +import com.sun.source.tree.LineMap; +import com.sun.source.util.TaskEvent; +import com.sun.source.util.TaskListener; +import com.sun.tools.javac.api.BasicJavacTask; +import com.sun.tools.javac.tree.EndPosTable; +import com.sun.tools.javac.tree.JCTree; +import com.sun.tools.javac.tree.TreeMaker; +import com.sun.tools.javac.util.Context; +import com.sun.tools.javac.util.List; +import com.sun.tools.javac.util.Log; +import com.sun.tools.javac.util.Names; +import java.io.PrintWriter; +import java.net.URI; +import java.nio.file.Path; +import java.nio.file.Paths; +import javax.tools.JavaFileObject; + +final class DatadogTaskListener implements TaskListener { + private final BasicJavacTask basicJavacTask; + private final boolean methodAnnotationDisabled; + + DatadogTaskListener(BasicJavacTask basicJavacTask, boolean methodAnnotationDisabled) { + this.basicJavacTask = basicJavacTask; + this.methodAnnotationDisabled = methodAnnotationDisabled; + } + + @Override + public void started(TaskEvent e) { + } + + @Override + public void finished(TaskEvent e) { + TaskEvent.Kind taskKind = e.getKind(); + if (taskKind != TaskEvent.Kind.PARSE) { + return; + } + + Context context = basicJavacTask.getContext(); + try { + Path sourcePath = getSourcePath(e); + if (sourcePath.endsWith("module-info.java")) { + ModuleOpeningClassVisitor moduleOpeningClassVisitor = new ModuleOpeningClassVisitor(context); + CompilationUnitTree compilationUnit = e.getCompilationUnit(); + compilationUnit.accept(moduleOpeningClassVisitor, null); + return; + } + + TreeMaker maker = TreeMaker.instance(context); + Names names = Names.instance(context); + + JCTree.JCExpression sourcePathAnnotationType = select(maker, names, "datadog", "compiler", "annotations", "SourcePath"); + JCTree.JCAnnotation sourcePathAnnotation = annotation(maker, sourcePathAnnotationType, maker.Literal(sourcePath.toString())); + + JCTree.JCExpression methodLinesAnnotationType = select(maker, names, "datadog", "compiler", "annotations", "MethodLines"); + + CompilationUnitTree compilationUnit = e.getCompilationUnit(); + LineMap lineMap = compilationUnit.getLineMap(); + EndPosTable endPositions; + if (compilationUnit instanceof JCTree.JCCompilationUnit) { + JCTree.JCCompilationUnit jcCompilationUnit = (JCTree.JCCompilationUnit) compilationUnit; + endPositions = jcCompilationUnit.endPositions; + } else { + endPositions = null; + } + + AnnotationsInjectingClassVisitor treeVisitor = new AnnotationsInjectingClassVisitor( + maker, names, sourcePathAnnotation, methodLinesAnnotationType, methodAnnotationDisabled, lineMap, endPositions); + compilationUnit.accept(treeVisitor, null); + + } catch (Throwable t) { + Log log = Log.instance(context); + log.printRawLines( + Log.WriterKind.WARNING, + "Could not process " + + log.currentSourceFile().toUri() + + ": " + + t.getMessage()); + + PrintWriter logWriter = log.getWriter(Log.WriterKind.WARNING); + t.printStackTrace(logWriter); + } + } + + private static Path getSourcePath(TaskEvent e) { + JavaFileObject sourceFile = e.getSourceFile(); + URI sourceUri = sourceFile.toUri(); + return Paths.get(sourceUri).toAbsolutePath(); + } + + private static JCTree.JCExpression select(TreeMaker maker, Names names, String... parts) { + JCTree.JCExpression id = maker.Ident(names.fromString(parts[0])); + for (int i = 1; i < parts.length; i++) { + id = maker.Select(id, names.fromString(parts[i])); + } + return id; + } + + private static JCTree.JCAnnotation annotation(TreeMaker maker, JCTree type, JCTree.JCExpression... arguments) { + return maker.Annotation(type, List.from(arguments)); + } +} diff --git a/src/main/java/datadog/compiler/ModuleOpeningClassVisitor.java b/src/main/java/datadog/compiler/ModuleOpeningClassVisitor.java new file mode 100644 index 0000000..b99feec --- /dev/null +++ b/src/main/java/datadog/compiler/ModuleOpeningClassVisitor.java @@ -0,0 +1,12 @@ +package datadog.compiler; + +import com.sun.source.util.TreeScanner; +import com.sun.tools.javac.util.Context; + +public class ModuleOpeningClassVisitor extends TreeScanner { + + public ModuleOpeningClassVisitor(Context context) { + // this is a stub used only for JDK 8. It does nothing. See corresponding class in JDK 9+ sources. + } + +} diff --git a/src/main/java/datadog/compiler/UnnamedModuleOpener.java b/src/main/java/datadog/compiler/UnnamedModuleOpener.java deleted file mode 100644 index 749e9a0..0000000 --- a/src/main/java/datadog/compiler/UnnamedModuleOpener.java +++ /dev/null @@ -1,10 +0,0 @@ -package datadog.compiler; - -import com.sun.tools.javac.util.Context; - -public class UnnamedModuleOpener { - - public static void open(Context context) { - // no op - } -} diff --git a/src/main/java9/datadog/compiler/CompilerModuleOpener.java b/src/main/java9/datadog/compiler/CompilerModuleOpener.java new file mode 100644 index 0000000..1bdd716 --- /dev/null +++ b/src/main/java9/datadog/compiler/CompilerModuleOpener.java @@ -0,0 +1,34 @@ +package datadog.compiler; + +import java.util.concurrent.Executor; +import org.burningwave.core.assembler.StaticComponentContainer; +import org.burningwave.core.function.ThrowingRunnable; + +public class CompilerModuleOpener { + + /** + * Exports {@code jdk.compiler} module to unnamed modules. + *

+ * The code of the plugin lives in an unnamed module. + * It needs to access the API from the {@code jdk.compiler} module. + *

+ * There are a few ways of ensuring this: + *

    + *
  • Make the plugin users declare declare --add-exports.
  • + *
  • Move the plugin classes to a named module and specify that the module requires access to JDK compiler (it looks like some of the APIs we need to access are not exported, so this solution is unlikely to work).
  • + *
  • Use the hack implemented in this method (it modifies an internal field in a core JDK class with the use of reflection, to add the necessary {@code --add-exports} as if they were provided in the javac command).
  • + *
+ */ + public static void setup() { + try { + if (StaticComponentContainer.JVMInfo.getVersion() >= 16) { + StaticComponentContainer.Modules.exportToAllUnnamed("jdk.compiler"); + } + // force classes to be loaded: https://github.com/burningwave/core/discussions/15 + ThrowingRunnable.class.getClassLoader(); + Executor.class.getClassLoader(); + } catch (Throwable e) { + // ignore + } + } +} diff --git a/src/main/java9/datadog/compiler/ModuleOpeningClassVisitor.java b/src/main/java9/datadog/compiler/ModuleOpeningClassVisitor.java new file mode 100644 index 0000000..083ceb2 --- /dev/null +++ b/src/main/java9/datadog/compiler/ModuleOpeningClassVisitor.java @@ -0,0 +1,53 @@ +package datadog.compiler; + +import com.sun.source.tree.ExpressionTree; +import com.sun.source.tree.ModuleTree; +import com.sun.source.util.TreeScanner; +import com.sun.tools.javac.comp.Modules; +import com.sun.tools.javac.util.Context; +import java.lang.reflect.Field; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +/** + * Opens unnamed module (which contains the injected source path annotation) + * to the other non-system modules that constitute the compilation unit. + *

+ * The module is opened by programmatically modifying the {@code --add-reads} option. + *

+ * The goal is to avoid compiler warnings saying that source path annotation class + * cannot be found when referenced by a class in a different module + * (some projects are set up in a way that cause build failure whenever a compiler warning is encountered). + */ +public class ModuleOpeningClassVisitor extends TreeScanner { + + private static final ConcurrentMap PROCESSED_MODULES = new ConcurrentHashMap<>(); + + private final Modules modules; + + public ModuleOpeningClassVisitor(Context context) { + modules = Modules.instance(context); + } + + @Override + public Void visitModule(ModuleTree node, Void unused) { + ExpressionTree nodeName = node.getName(); + PROCESSED_MODULES.computeIfAbsent(nodeName.toString(), this::augmentAddReadsOption); + return super.visitModule(node, unused); + } + + private boolean augmentAddReadsOption(String moduleName) { + try { + Field addReadsOptField = Modules.class.getDeclaredField("addReadsOpt"); + addReadsOptField.setAccessible(true); + + String currentValue = (String) addReadsOptField.get(modules); + String newValue = (currentValue != null ? currentValue + '\0' : "") + String.format("%s=ALL-UNNAMED", moduleName); + + addReadsOptField.set(modules, newValue); + } catch (Exception e) { + // ignore + } + return true; + } +} diff --git a/src/main/java9/datadog/compiler/UnnamedModuleOpener.java b/src/main/java9/datadog/compiler/UnnamedModuleOpener.java deleted file mode 100644 index 63f0576..0000000 --- a/src/main/java9/datadog/compiler/UnnamedModuleOpener.java +++ /dev/null @@ -1,82 +0,0 @@ -package datadog.compiler; - -import com.sun.tools.javac.code.ModuleFinder; -import com.sun.tools.javac.code.Symbol; -import com.sun.tools.javac.comp.Modules; -import com.sun.tools.javac.util.Context; -import com.sun.tools.javac.util.List; -import com.sun.tools.javac.util.Name; -import com.sun.tools.javac.util.Names; -import java.lang.reflect.Field; -import javax.tools.JavaFileManager; -import javax.tools.JavaFileObject; -import javax.tools.StandardLocation; - -public class UnnamedModuleOpener { - - /** - * Opens unnamed module (which contains the injected source path annotation) - * to the other non-system modules that constitute or are referenced by the compilation unit. - *

- * The module is opened by programmatically modifying the {@code --add-reads} option. - *

- * The goal is to avoid compiler warnings saying that source path annotation class - * cannot be found when referenced by a class in a different module - */ - public static void open(Context context) { - StringBuilder addReads = new StringBuilder(); - - ModuleFinder moduleFinder = ModuleFinder.instance(context); - List allModules = moduleFinder.findAllModules(); - for (Symbol.ModuleSymbol module : allModules) { - String systemModulesLocation = StandardLocation.SYSTEM_MODULES.name(); - boolean systemModule = module.classLocation.toString().startsWith(systemModulesLocation); - if (!systemModule) { - String moduleName = module.getQualifiedName().toString(); - appendOpenUnnamedModuleArgument(addReads, moduleName); - } - } - - String currentModuleName = getCurrentModuleName(context); - if (currentModuleName != null) { - appendOpenUnnamedModuleArgument(addReads, currentModuleName); - } - - augmentAddReadsOption(context, addReads.toString()); - } - - private static void appendOpenUnnamedModuleArgument(StringBuilder addReads, String moduleName) { - addReads.append(moduleName).append('=').append("ALL-UNNAMED").append('\0'); - } - - private static String getCurrentModuleName(Context context) { - JavaFileManager javaFileManager = context.get(JavaFileManager.class); - if (javaFileManager.hasLocation(StandardLocation.SOURCE_PATH)) { - Names names = Names.instance(context); - try { - JavaFileObject currentModuleInfo = javaFileManager.getJavaFileForInput(StandardLocation.SOURCE_PATH, names.module_info.toString(), JavaFileObject.Kind.SOURCE); - ModuleFinder moduleFinder = ModuleFinder.instance(context); - Name currentModuleName = moduleFinder.moduleNameFromSourceReader.readModuleName(currentModuleInfo); - return currentModuleName.toString(); - } catch (Exception e) { - // ignore - } - } - return null; - } - - private static void augmentAddReadsOption(Context context, String addReads) { - Modules modules = Modules.instance(context); - try { - Field addReadsOptField = Modules.class.getDeclaredField("addReadsOpt"); - addReadsOptField.setAccessible(true); - - String currentValue = (String) addReadsOptField.get(modules); - String augmentedValue = (currentValue != null ? currentValue + '\0' : "") + addReads; - - addReadsOptField.set(modules, augmentedValue); - } catch (Exception e) { - // ignore - } - } -}