diff --git a/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/core/dom/JavacCompilationUnitResolver.java b/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/core/dom/JavacCompilationUnitResolver.java index 684eab35d1d..782222108e8 100644 --- a/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/core/dom/JavacCompilationUnitResolver.java +++ b/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/core/dom/JavacCompilationUnitResolver.java @@ -451,6 +451,9 @@ public CompilationUnit toCompilationUnit(org.eclipse.jdt.internal.compiler.env.I } var pathToUnit = new HashMap(); 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); diff --git a/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/javac/JavacClassFile.java b/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/javac/JavacClassFile.java index 79e094e77ff..9120660947a 100644 --- a/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/javac/JavacClassFile.java +++ b/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/javac/JavacClassFile.java @@ -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; @@ -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("\\."); diff --git a/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/javac/JavacCompiler.java b/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/javac/JavacCompiler.java index 47a58deae7a..93e0d2320d3 100644 --- a/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/javac/JavacCompiler.java +++ b/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/javac/JavacCompiler.java @@ -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; @@ -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; @@ -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; @@ -61,6 +61,8 @@ public class JavacCompiler extends Compiler { JavacConfig compilerConfig; IProblemFactory problemFactory; + Map fileObjectToCUMap = new HashMap<>(); + public JavacCompiler(INameEnvironment environment, IErrorHandlingPolicy policy, CompilerConfiguration compilerConfig, ICompilerRequestor requestor, IProblemFactory problemFactory) { super(environment, policy, compilerConfig.compilerOptions(), requestor, problemFactory); @@ -74,13 +76,17 @@ public void compile(ICompilationUnit[] sourceUnits) { Map> 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 previous = javacProblems.get(fileObject.getOriginalUnit()); + ICompilationUnit originalUnit = this.fileObjectToCUMap.get(fileObject); + if (originalUnit == null) { + return; + } + List previous = javacProblems.get(originalUnit); if (previous == null) { previous = new ArrayList<>(); - javacProblems.put(fileObject.getOriginalUnit(), previous); + javacProblems.put(originalUnit, previous); } previous.add(javacProblem); } @@ -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); } } @@ -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); diff --git a/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/javac/JavacFileObject.java b/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/javac/JavacFileObject.java deleted file mode 100644 index 4b77f225893..00000000000 --- a/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/javac/JavacFileObject.java +++ /dev/null @@ -1,33 +0,0 @@ -/******************************************************************************* -* Copyright (c) 2024 Microsoft Corporation and others. -* All rights reserved. This program and the accompanying materials -* are made available under the terms of the Eclipse Public License 2.0 -* which accompanies this distribution, and is available at -* https://www.eclipse.org/legal/epl-2.0/ -* -* SPDX-License-Identifier: EPL-2.0 -* -* Contributors: -* Microsoft Corporation - initial API and implementation -*******************************************************************************/ - -package org.eclipse.jdt.internal.javac; - -import java.net.URI; -import java.nio.charset.Charset; - -import org.eclipse.jdt.internal.compiler.env.ICompilationUnit; -import org.eclipse.jdt.internal.compiler.tool.EclipseFileObject; - -public class JavacFileObject extends EclipseFileObject { - private ICompilationUnit originalUnit; - - public JavacFileObject(ICompilationUnit originalUnit, String className, URI uri, Kind kind, Charset charset) { - super(className, uri, kind, charset); - this.originalUnit = originalUnit; - } - - public ICompilationUnit getOriginalUnit() { - return this.originalUnit; - } -} diff --git a/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/javac/JavacProblemConverter.java b/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/javac/JavacProblemConverter.java index b38acb826a9..2d92a38d858 100644 --- a/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/javac/JavacProblemConverter.java +++ b/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/javac/JavacProblemConverter.java @@ -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); @@ -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")) { diff --git a/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/javac/JavacTaskListener.java b/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/javac/JavacTaskListener.java index 73609209d36..0ff518ed46b 100644 --- a/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/javac/JavacTaskListener.java +++ b/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/javac/JavacTaskListener.java @@ -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 sourceOutputMapping = new HashMap<>(); private Map results = new HashMap<>(); private UnusedProblemFactory problemFactory; + private final Map fileObjectToCUMap; private static final Set PRIMITIVE_TYPES = new HashSet(Arrays.asList( "byte", "short", @@ -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> outputSourceMapping, - IProblemFactory problemFactory) { + IProblemFactory problemFactory, Map fileObjectToCUMap) { this.problemFactory = new UnusedProblemFactory(problemFactory, config.compilerOptions()); + this.fileObjectToCUMap = fileObjectToCUMap; for (Entry> entry : outputSourceMapping.entrySet()) { IContainer currentOutput = entry.getKey(); entry.getValue().forEach(cu -> sourceOutputMapping.put(cu, currentOutput)); @@ -78,17 +83,27 @@ public JavacTaskListener(JavacConfig config, Map new JavacCompilationResult(cu1)); final Map visitedClasses = new HashMap(); final Set hierarchyRecorded = new HashSet<>(); final TypeElement currentTopLevelType = e.getTypeElement(); UnusedTreeScanner 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) { diff --git a/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/javac/JavacUtils.java b/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/javac/JavacUtils.java index 4328adc87c1..d80ce5fabdc 100644 --- a/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/javac/JavacUtils.java +++ b/org.eclipse.jdt.core.javac/src/org/eclipse/jdt/internal/javac/JavacUtils.java @@ -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; @@ -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; @@ -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, @@ -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() @@ -252,7 +256,37 @@ private static void configurePaths(JavaProject javaProject, Context context, Jav classpathEnabled = true; } if (!classpathEnabled) { + Set 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); @@ -270,14 +304,22 @@ private static Collection 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); + } } } }