Skip to content

Commit

Permalink
Fix ClassCastException when working with module projects
Browse files Browse the repository at this point in the history
- Remove `JavacFileObject`, use file objects from file manager

Signed-off-by: David Thompson <davthomp@redhat.com>
  • Loading branch information
datho7561 committed Sep 27, 2024
1 parent 2769b50 commit 4ba5329
Show file tree
Hide file tree
Showing 7 changed files with 104 additions and 62 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,9 @@ public CompilationUnit toCompilationUnit(org.eclipse.jdt.internal.compiler.env.I
}
var pathToUnit = new HashMap<String, org.eclipse.jdt.internal.compiler.env.ICompilationUnit>();
Arrays.stream(workingCopies) //
.filter(inMemoryCu -> {
return project == null || (inMemoryCu.getElementName() != null && !inMemoryCu.getElementName().contains("module-info")) || inMemoryCu.getJavaProject() == project;
})
.map(org.eclipse.jdt.internal.compiler.env.ICompilationUnit.class::cast) //
.forEach(inMemoryCu -> {
pathToUnit.put(new String(inMemoryCu.getFileName()), inMemoryCu);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
import org.eclipse.jdt.internal.compiler.ClassFile;
import org.eclipse.jdt.internal.compiler.util.SuffixConstants;

import com.sun.tools.javac.tree.JCTree.JCModuleDecl;

public class JavacClassFile extends ClassFile {
private String fullName;
private IContainer outputDir;
Expand All @@ -40,6 +42,14 @@ public JavacClassFile(String qualifiedName, ClassFile enclosingClass, IContainer
this.outputDir = outputDir;
}

public JavacClassFile(JCModuleDecl moduleDecl, IContainer outputDir) {
// TODO: moduleDecl probably needs to be used, but how?
this.fullName = "module-info";
this.isNestedType = false;
this.enclosingClassFile = null;
this.outputDir = outputDir;
}

@Override
public char[][] getCompoundName() {
String[] names = this.fullName.split("\\.");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
package org.eclipse.jdt.internal.javac;

import java.io.File;
import java.nio.charset.Charset;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
Expand All @@ -24,8 +23,8 @@
import java.util.stream.Stream;

import javax.tools.DiagnosticListener;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;
import javax.tools.JavaFileObject.Kind;

import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IResource;
Expand All @@ -51,6 +50,7 @@
import com.sun.tools.javac.comp.AttrContext;
import com.sun.tools.javac.comp.CompileStates.CompileState;
import com.sun.tools.javac.comp.Env;
import com.sun.tools.javac.file.JavacFileManager;
import com.sun.tools.javac.main.JavaCompiler;
import com.sun.tools.javac.tree.JCTree.JCClassDecl;
import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
Expand All @@ -61,6 +61,8 @@ public class JavacCompiler extends Compiler {
JavacConfig compilerConfig;
IProblemFactory problemFactory;

Map<JavaFileObject, ICompilationUnit> fileObjectToCUMap = new HashMap<>();

public JavacCompiler(INameEnvironment environment, IErrorHandlingPolicy policy, CompilerConfiguration compilerConfig,
ICompilerRequestor requestor, IProblemFactory problemFactory) {
super(environment, policy, compilerConfig.compilerOptions(), requestor, problemFactory);
Expand All @@ -74,13 +76,17 @@ public void compile(ICompilationUnit[] sourceUnits) {
Map<ICompilationUnit, List<IProblem>> javacProblems = new HashMap<>();
JavacProblemConverter problemConverter = new JavacProblemConverter(this.compilerConfig.compilerOptions(), javacContext);
javacContext.put(DiagnosticListener.class, diagnostic -> {
if (diagnostic.getSource() instanceof JavacFileObject fileObject) {
if (diagnostic.getSource() instanceof JavaFileObject fileObject) {
JavacProblem javacProblem = problemConverter.createJavacProblem(diagnostic);
if (javacProblem != null) {
List<IProblem> previous = javacProblems.get(fileObject.getOriginalUnit());
ICompilationUnit originalUnit = this.fileObjectToCUMap.get(fileObject);
if (originalUnit == null) {
return;
}
List<IProblem> previous = javacProblems.get(originalUnit);
if (previous == null) {
previous = new ArrayList<>();
javacProblems.put(fileObject.getOriginalUnit(), previous);
javacProblems.put(originalUnit, previous);
}
previous.add(javacProblem);
}
Expand Down Expand Up @@ -113,13 +119,13 @@ public void compile(ICompilationUnit[] sourceUnits) {
.collect(Collectors.groupingBy(this::computeOutputDirectory));

// Register listener to intercept intermediate results from Javac task.
JavacTaskListener javacListener = new JavacTaskListener(this.compilerConfig, outputSourceMapping, this.problemFactory);
JavacTaskListener javacListener = new JavacTaskListener(this.compilerConfig, outputSourceMapping, this.problemFactory, this.fileObjectToCUMap);
MultiTaskListener mtl = MultiTaskListener.instance(javacContext);
mtl.add(javacListener);
mtl.add(new TaskListener() {
@Override
public void finished(TaskEvent e) {
if (e.getSourceFile() instanceof JavacFileObject && e.getCompilationUnit() instanceof JCCompilationUnit u) {
if (e.getSourceFile() != null && fileObjectToCUMap.get(e.getSourceFile()) instanceof JCCompilationUnit u) {
problemConverter.registerUnit(e.getSourceFile(), u);
}
}
Expand Down Expand Up @@ -170,25 +176,23 @@ public int errorCount() {
};
javacContext.put(JavaCompiler.compilerKey, javac);
javac.shouldStopPolicyIfError = CompileState.GENERATE;
JavacFileManager fileManager = (JavacFileManager)javacContext.get(JavaFileManager.class);
try {
javac.compile(com.sun.tools.javac.util.List.from(outputSourceSet.getValue().stream()
.filter(SourceFile.class::isInstance).map(SourceFile.class::cast).map(source -> {
File unitFile;
if (javaProject != null) {
// path is relative to the workspace, make it absolute
IResource asResource = javaProject.getProject().getParent()
.findMember(new String(source.getFileName()));
if (asResource != null) {
unitFile = asResource.getLocation().toFile();
} else {
unitFile = new File(new String(source.getFileName()));
}
// path is relative to the workspace, make it absolute
IResource asResource = javaProject.getProject().getParent()
.findMember(new String(source.getFileName()));
if (asResource != null) {
unitFile = asResource.getLocation().toFile();
} else {
unitFile = new File(new String(source.getFileName()));
}
return new JavacFileObject(source, null, unitFile.toURI(), Kind.SOURCE,
Charset.defaultCharset());
}).map(JavaFileObject.class::cast).toList()));
JavaFileObject jfo = fileManager.getJavaFileObject(unitFile.getAbsolutePath());
fileObjectToCUMap.put(jfo, source);
return jfo;
}).toList()));
} catch (Throwable e) {
// TODO fail
ILog.get().error("compilation failed", e);
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -429,7 +429,7 @@ private org.eclipse.jface.text.Position getDiagnosticPosition(JCDiagnostic jcDia
return getDefaultPosition(jcDiagnostic);
}
private org.eclipse.jface.text.Position getDefaultPosition(Diagnostic<?> diagnostic) {
if (diagnostic.getPosition() > 0) {
if (diagnostic.getPosition() >= 0) {
int start = (int) Math.min(diagnostic.getPosition(), diagnostic.getStartPosition());
int end = (int) Math.max(diagnostic.getEndPosition(), start);
return new org.eclipse.jface.text.Position(start, end - start);
Expand Down Expand Up @@ -1069,6 +1069,7 @@ yield switch (rootCauseCode) {
case "compiler.err.incorrect.receiver.type" -> IProblem.IllegalTypeForExplicitThis;
case "compiler.err.incorrect.constructor.receiver.type" -> IProblem.IllegalTypeForExplicitThis;
case "compiler.err.incorrect.constructor.receiver.name" -> IProblem.IllegalQualifierForExplicitThis;
case "compiler.err.too.many.modules" -> IProblem.ModuleRelated;
default -> {
ILog.get().error("Could not accurately convert diagnostic (" + diagnostic.getCode() + ")\n" + diagnostic);
if (diagnostic.getKind() == javax.tools.Diagnostic.Kind.ERROR && diagnostic.getCode().startsWith("compiler.err")) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,14 @@
import com.sun.tools.javac.code.Type.UnknownType;
import com.sun.tools.javac.tree.JCTree.JCClassDecl;
import com.sun.tools.javac.tree.JCTree.JCFieldAccess;
import com.sun.tools.javac.tree.JCTree.JCModuleDecl;
import com.sun.tools.javac.tree.JCTree.JCIdent;

public class JavacTaskListener implements TaskListener {
private Map<ICompilationUnit, IContainer> sourceOutputMapping = new HashMap<>();
private Map<ICompilationUnit, JavacCompilationResult> results = new HashMap<>();
private UnusedProblemFactory problemFactory;
private final Map<JavaFileObject, ICompilationUnit> fileObjectToCUMap;
private static final Set<String> PRIMITIVE_TYPES = new HashSet<String>(Arrays.asList(
"byte",
"short",
Expand All @@ -65,9 +67,12 @@ public class JavacTaskListener implements TaskListener {
"boolean"
));

private static final char[] MODULE_INFO_NAME = "module-info".toCharArray();

public JavacTaskListener(JavacConfig config, Map<IContainer, List<ICompilationUnit>> outputSourceMapping,
IProblemFactory problemFactory) {
IProblemFactory problemFactory, Map<JavaFileObject, ICompilationUnit> fileObjectToCUMap) {
this.problemFactory = new UnusedProblemFactory(problemFactory, config.compilerOptions());
this.fileObjectToCUMap = fileObjectToCUMap;
for (Entry<IContainer, List<ICompilationUnit>> entry : outputSourceMapping.entrySet()) {
IContainer currentOutput = entry.getKey();
entry.getValue().forEach(cu -> sourceOutputMapping.put(cu, currentOutput));
Expand All @@ -78,17 +83,27 @@ public JavacTaskListener(JavacConfig config, Map<IContainer, List<ICompilationUn
public void finished(TaskEvent e) {
if (e.getKind() == TaskEvent.Kind.ANALYZE) {
final JavaFileObject file = e.getSourceFile();
if (!(file instanceof JavacFileObject)) {
final ICompilationUnit cu = this.fileObjectToCUMap.get(file);
if (cu == null) {
return;
}

final ICompilationUnit cu = ((JavacFileObject) file).getOriginalUnit();
final JavacCompilationResult result = this.results.computeIfAbsent(cu, (cu1) ->
new JavacCompilationResult(cu1));
final Map<Symbol, ClassFile> visitedClasses = new HashMap<Symbol, ClassFile>();
final Set<ClassSymbol> hierarchyRecorded = new HashSet<>();
final TypeElement currentTopLevelType = e.getTypeElement();
UnusedTreeScanner<Void, Void> scanner = new UnusedTreeScanner<>() {

@Override
public Void visitModule(com.sun.source.tree.ModuleTree node, Void p) {
if (node instanceof JCModuleDecl moduleDecl) {
IContainer expectedOutputDir = sourceOutputMapping.get(cu);
ClassFile currentClass = new JavacClassFile(moduleDecl, expectedOutputDir);
result.record(MODULE_INFO_NAME, currentClass);
}
return super.visitModule(node, p);
}

@Override
public Void visitClass(ClassTree node, Void p) {
if (node instanceof JCClassDecl classDecl) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Queue;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import javax.tools.JavaFileManager;
import javax.tools.StandardLocation;
Expand All @@ -36,6 +38,7 @@
import org.eclipse.jdt.core.IClasspathAttribute;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IModuleDescription;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
Expand Down Expand Up @@ -233,7 +236,7 @@ private static void configurePaths(JavaProject javaProject, Context context, Jav
if (!sourcePathEnabled) {
fileManager.setLocation(StandardLocation.SOURCE_PATH, classpathEntriesToFiles(javaProject, entry -> entry.getEntryKind() == IClasspathEntry.CPE_SOURCE && (isTest || !entry.isTest())));
}

boolean classpathEnabled = false;
if (compilerConfig != null && !isEmpty(compilerConfig.classpaths())) {
fileManager.setLocation(StandardLocation.CLASS_PATH,
Expand All @@ -243,6 +246,7 @@ private static void configurePaths(JavaProject javaProject, Context context, Jav
.toList());
classpathEnabled = true;
}

if (compilerConfig != null && !isEmpty(compilerConfig.modulepaths())) {
fileManager.setLocation(StandardLocation.MODULE_PATH,
compilerConfig.modulepaths()
Expand All @@ -252,7 +256,37 @@ private static void configurePaths(JavaProject javaProject, Context context, Jav
classpathEnabled = true;
}
if (!classpathEnabled) {
Set<JavaProject> moduleProjects = Stream.of(javaProject.getExpandedClasspath())
.filter(classpath -> classpath.getEntryKind() == IClasspathEntry.CPE_PROJECT)
.map(classpath -> javaProject.getJavaModel().getJavaProject(classpath.getPath().lastSegment()))
.filter(Objects::nonNull)
.filter(classpathJavaProject -> {
try {
return classpathJavaProject.getModuleDescription() != null;
} catch (JavaModelException e) {
return false;
}
})
.collect(Collectors.toSet());

fileManager.setLocation(StandardLocation.CLASS_PATH, classpathEntriesToFiles(javaProject, entry -> entry.getEntryKind() != IClasspathEntry.CPE_SOURCE && (isTest || !entry.isTest())));

if (!moduleProjects.isEmpty()) {
fileManager.setLocation(StandardLocation.MODULE_PATH, moduleProjects.stream()
.map(project -> {
try {
IPath relativeOutputPath = project.getOutputLocation();
IPath absPath = javaProject.getProject().getParent()
.findMember(relativeOutputPath).getLocation();
return absPath.toOSString();
} catch (JavaModelException e) {
return null;
}
})
.filter(Objects::nonNull)
.map(File::new)
.toList());
}
}
} catch (Exception ex) {
ILog.get().error(ex.getMessage(), ex);
Expand All @@ -270,14 +304,22 @@ private static Collection<File> classpathEntriesToFiles(JavaProject project, Pre
toProcess.addAll(Arrays.asList(project.resolveClasspath(project.getExpandedClasspath())));
while (!toProcess.isEmpty()) {
IClasspathEntry current = toProcess.poll();
if (current.getEntryKind() == IClasspathEntry.CPE_PROJECT) {
if (current.getEntryKind() == IClasspathEntry.CPE_PROJECT && select.test(current)) {
IResource referencedResource = project.getProject().getParent().findMember(current.getPath());
if (referencedResource instanceof IProject referencedProject) {
JavaProject referencedJavaProject = (JavaProject) JavaCore.create(referencedProject);
if (referencedJavaProject.exists()) {
for (IClasspathEntry transitiveEntry : referencedJavaProject.resolveClasspath(referencedJavaProject.getExpandedClasspath()) ) {
if (transitiveEntry.isExported() || transitiveEntry.getEntryKind() == IClasspathEntry.CPE_SOURCE) {
toProcess.add(transitiveEntry);
IModuleDescription moduleDescription = null;
try {
moduleDescription = referencedJavaProject.getModuleDescription();
} catch (JavaModelException e) {
// do nothing
}
if (moduleDescription == null) {
for (IClasspathEntry transitiveEntry : referencedJavaProject.resolveClasspath(referencedJavaProject.getExpandedClasspath()) ) {
if (transitiveEntry.isExported() || transitiveEntry.getEntryKind() == IClasspathEntry.CPE_SOURCE) {
toProcess.add(transitiveEntry);
}
}
}
}
Expand Down

0 comments on commit 4ba5329

Please sign in to comment.