diff --git a/org.eclipse.jdt.compiler.tool.tests/resources/module_locations/GH958/module-info.java b/org.eclipse.jdt.compiler.tool.tests/resources/module_locations/GH958/module-info.java new file mode 100644 index 00000000000..56d94ed4572 --- /dev/null +++ b/org.eclipse.jdt.compiler.tool.tests/resources/module_locations/GH958/module-info.java @@ -0,0 +1,3 @@ +module org.example { + requires org.example.adder; +} \ No newline at end of file diff --git a/org.eclipse.jdt.compiler.tool.tests/resources/module_locations/GH958/org/example/impl/AddNumbers.java b/org.eclipse.jdt.compiler.tool.tests/resources/module_locations/GH958/org/example/impl/AddNumbers.java new file mode 100644 index 00000000000..1db4b12517d --- /dev/null +++ b/org.eclipse.jdt.compiler.tool.tests/resources/module_locations/GH958/org/example/impl/AddNumbers.java @@ -0,0 +1,11 @@ +package org.example.impl; + +import org.example.adder.Adder; + +public class AddNumbers { + public static void main(String[] args) { + int a = 123; + int b = 456; + System.out.println(new Adder().add(a, b)); + } +} diff --git a/org.eclipse.jdt.compiler.tool.tests/resources/module_locations/GH958_2/module-info.java b/org.eclipse.jdt.compiler.tool.tests/resources/module_locations/GH958_2/module-info.java new file mode 100644 index 00000000000..277a588f25d --- /dev/null +++ b/org.eclipse.jdt.compiler.tool.tests/resources/module_locations/GH958_2/module-info.java @@ -0,0 +1,4 @@ +module GH958_2 { + requires GH958_mod; + requires org.example.adder; +} \ No newline at end of file diff --git a/org.eclipse.jdt.compiler.tool.tests/resources/module_locations/GH958_2/org/example2/Main.java b/org.eclipse.jdt.compiler.tool.tests/resources/module_locations/GH958_2/org/example2/Main.java new file mode 100644 index 00000000000..b41f4c7bb0a --- /dev/null +++ b/org.eclipse.jdt.compiler.tool.tests/resources/module_locations/GH958_2/org/example2/Main.java @@ -0,0 +1,13 @@ +package org.example2; + +import org.example.regular.Foo; +import org.example.adder.Adder; + +public class Main { + Foo foo; + public static void main(String[] args) { + int a = 123; + int b = 456; + System.out.println(new Adder().add(a, b)); + } +} diff --git a/org.eclipse.jdt.compiler.tool.tests/resources/module_locations/GH958_automod.jar b/org.eclipse.jdt.compiler.tool.tests/resources/module_locations/GH958_automod.jar new file mode 100644 index 00000000000..b7a0bc13474 Binary files /dev/null and b/org.eclipse.jdt.compiler.tool.tests/resources/module_locations/GH958_automod.jar differ diff --git a/org.eclipse.jdt.compiler.tool.tests/resources/module_locations/GH958_mod.jar b/org.eclipse.jdt.compiler.tool.tests/resources/module_locations/GH958_mod.jar new file mode 100644 index 00000000000..2198d53df8d Binary files /dev/null and b/org.eclipse.jdt.compiler.tool.tests/resources/module_locations/GH958_mod.jar differ diff --git a/org.eclipse.jdt.compiler.tool.tests/src/org/eclipse/jdt/compiler/tool/tests/CompilerToolJava9Tests.java b/org.eclipse.jdt.compiler.tool.tests/src/org/eclipse/jdt/compiler/tool/tests/CompilerToolJava9Tests.java index d3217f5e840..6b9f04fcea3 100644 --- a/org.eclipse.jdt.compiler.tool.tests/src/org/eclipse/jdt/compiler/tool/tests/CompilerToolJava9Tests.java +++ b/org.eclipse.jdt.compiler.tool.tests/src/org/eclipse/jdt/compiler/tool/tests/CompilerToolJava9Tests.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017, 2021 IBM Corporation and others. + * Copyright (c) 2017, 2024 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -21,6 +21,9 @@ import java.io.PrintWriter; import java.io.StringWriter; import java.io.Writer; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; import java.net.URL; import java.nio.charset.Charset; import java.nio.file.Files; @@ -954,6 +957,93 @@ public void testBug533830_1() throws IOException { //-- We must now also have a diagnostic assertTrue("The diagnostic listener did not receive an error for the illegal option", b.listener().hasDiagnostic("option -source is not supported when --release is used")); } + + /** + * Proxy that transparently delegates to a JavaFileManager impl, but hides the physical + * implementation of the delegated file manager to prevent ECJ performing analysis based + * on the superclasses of the file manager object. + */ + static class DemotingFileManagerProxy implements InvocationHandler { + private final JavaFileManager fileManager; + + private DemotingFileManagerProxy(JavaFileManager fileManager) { + this.fileManager = fileManager; + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + System.err.printf("Calling %s(%s)%n", method.getName(), Arrays.toString(args)); + try { + var result = method.invoke(fileManager, args); + System.err.printf("Call to %s(%s) returned %s%n", method.getName(), Arrays.toString(args), result); + return result; + } catch (Throwable ex) { + ex.printStackTrace(); + throw ex; + } + } + + public static JavaFileManager demote(JavaFileManager fileManager) { + return (JavaFileManager) Proxy.newProxyInstance( + fileManager.getClass().getClassLoader(), + new Class[]{ JavaFileManager.class }, + new DemotingFileManagerProxy(fileManager) + ); + } + } + public void testGH958() throws Exception { + File classOutput = new File(_tmpFolder); + JavaCompiler compiler = new EclipseCompiler(); + StandardJavaFileManager standardFileManager = compiler.getStandardFileManager(null, null, null); + + List modulePath = List.of(new File("resources/module_locations/GH958_automod.jar")); + List sourcePath = List.of(new File("resources/module_locations/GH958")); + standardFileManager.setLocation(StandardLocation.MODULE_PATH, modulePath); + standardFileManager.setLocation(StandardLocation.SOURCE_PATH, sourcePath); + standardFileManager.setLocation(StandardLocation.CLASS_OUTPUT, List.of(classOutput)); + + // Make ECJ think we don't inherit StandardJavaFileManager by wrapping it. + JavaFileManager demotedFileManager = DemotingFileManagerProxy.demote(standardFileManager); + Iterable compilationUnits = demotedFileManager.list(StandardLocation.SOURCE_PATH, "", Set.of(JavaFileObject.Kind.SOURCE), true); + + CompilationTask task = compiler.getTask( + null, + demotedFileManager, + null, + List.of("--release", "11", "-verbose"), + List.of(), + compilationUnits + ); + + assertTrue(task.call()); + } + public void testGH958_2modules() throws Exception { + File classOutput = new File(_tmpFolder); + JavaCompiler compiler = new EclipseCompiler(); + StandardJavaFileManager standardFileManager = compiler.getStandardFileManager(null, null, null); + + List modulesPath = List.of(new File("resources/module_locations/GH958_automod.jar"), + new File("resources/module_locations/GH958_mod.jar")); + List sourcePath = List.of(new File("resources/module_locations/GH958_2")); + standardFileManager.setLocation(StandardLocation.MODULE_PATH, modulesPath); + standardFileManager.setLocation(StandardLocation.SOURCE_PATH, sourcePath); + standardFileManager.setLocation(StandardLocation.CLASS_OUTPUT, List.of(classOutput)); + + // Make ECJ think we don't inherit StandardJavaFileManager by wrapping it. + JavaFileManager demotedFileManager = DemotingFileManagerProxy.demote(standardFileManager); + Iterable compilationUnits = demotedFileManager.list(StandardLocation.SOURCE_PATH, "", Set.of(JavaFileObject.Kind.SOURCE), true); + + CompilationTask task = compiler.getTask( + null, + demotedFileManager, + null, + List.of("--release", "11", "-verbose"), + List.of(), + compilationUnits + ); + + assertTrue(task.call()); + } /** * Helps with building a compiler invocation, handling the common parts of testing. diff --git a/org.eclipse.jdt.compiler.tool.tests/src/org/eclipse/jdt/compiler/tool/tests/InMemoryCompilationTest.java b/org.eclipse.jdt.compiler.tool.tests/src/org/eclipse/jdt/compiler/tool/tests/InMemoryCompilationTest.java index 2c8d7407e29..bf27441f4a7 100644 --- a/org.eclipse.jdt.compiler.tool.tests/src/org/eclipse/jdt/compiler/tool/tests/InMemoryCompilationTest.java +++ b/org.eclipse.jdt.compiler.tool.tests/src/org/eclipse/jdt/compiler/tool/tests/InMemoryCompilationTest.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2021 IBM Corporation and others. + * Copyright (c) 2021, 2024 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -191,7 +191,16 @@ public Iterable list(Location location, String packageName, Set< throws IOException { List result = new ArrayList<>(); if (location == StandardLocation.SOURCE_PATH && kinds.contains(Kind.SOURCE)) { - result.addAll(sources); + for (InMemoryJavaSourceFileObject sourceFileObject : sources) { + String name = sourceFileObject.getAbsClassName(); + int lastDot = name.lastIndexOf('.'); + if (lastDot == -1) + continue; + String packName = name.substring(0, lastDot-1); + boolean match = recurse ? packName.startsWith(packageName) : packName.equals(packageName); + if (match) + result.add(sourceFileObject); + } } if (super.hasLocation(location)) { Iterable superResult = super.list(location, packageName, kinds, recurse); diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/dispatch/BatchFilerImpl.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/dispatch/BatchFilerImpl.java index ad4924a85b2..9d36c6d68c2 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/dispatch/BatchFilerImpl.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/dispatch/BatchFilerImpl.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2006, 2018 BEA Systems, Inc. + * Copyright (c) 2006, 2024 BEA Systems, Inc. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -19,6 +19,7 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.net.URI; +import java.util.Collections; import java.util.HashSet; import javax.annotation.processing.Filer; import javax.annotation.processing.FilerException; @@ -28,8 +29,12 @@ import javax.tools.JavaFileManager; import javax.tools.JavaFileManager.Location; import javax.tools.JavaFileObject; +import javax.tools.JavaFileObject.Kind; import javax.tools.StandardLocation; +import org.eclipse.jdt.internal.compiler.batch.ClasspathJsr199; +import org.eclipse.jdt.internal.compiler.batch.Main; import org.eclipse.jdt.internal.compiler.env.ICompilationUnit; +import org.eclipse.jdt.internal.compiler.env.IModule; import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding; /** @@ -43,13 +48,32 @@ public class BatchFilerImpl implements Filer { protected final BatchProcessingEnvImpl _env; protected final JavaFileManager _fileManager; protected final HashSet _createdFiles; + protected String _moduleName; + protected String _encoding; - public BatchFilerImpl(BaseAnnotationProcessorManager dispatchManager, BatchProcessingEnvImpl env) + public BatchFilerImpl(BaseAnnotationProcessorManager dispatchManager, BatchProcessingEnvImpl env, Main main) { this._dispatchManager = dispatchManager; this._fileManager = env._fileManager; this._env = env; this._createdFiles = new HashSet<>(); + this._encoding = main.getDefaultEncoding(); + if (this._fileManager.hasLocation(StandardLocation.SOURCE_PATH)) { + try { + for (JavaFileObject javaFileObject : this._fileManager.list(StandardLocation.SOURCE_PATH, "", //$NON-NLS-1$ + Collections.singleton(Kind.SOURCE), false)) { + if (javaFileObject.getName().equals(IModule.MODULE_INFO_JAVA)) { + IModule module = ClasspathJsr199.extractModuleFromFileObject(javaFileObject, main::getNewParser, null, this._encoding); + if (module != null) + this._moduleName = String.valueOf(module.name()); + break; + } + } + } catch (IOException e) { + e.printStackTrace(); + main.logger.logException(e); + } + } } public void addNewUnit(ICompilationUnit unit) { @@ -74,7 +98,7 @@ public JavaFileObject createClassFile(CharSequence name, } this._createdFiles.add(uri); - return new HookedJavaFileObject(jfo, jfo.getName(), name.toString(), this); + return new HookedJavaFileObject(jfo, jfo.getName(), name.toString(), this, this._moduleName, this._encoding); } /* (non-Javadoc) @@ -146,7 +170,13 @@ public JavaFileObject createSourceFile(CharSequence name, if (typeElement != null) { throw new FilerException("Source file already exists : " + moduleAndPkgString); //$NON-NLS-1$ } - Location location = mod == null ? StandardLocation.SOURCE_OUTPUT : this._fileManager.getLocationForModule(StandardLocation.SOURCE_OUTPUT, mod); + Location location; + if (mod == null) { + location = StandardLocation.SOURCE_OUTPUT; + mod = this._moduleName; + } else { + location = this._fileManager.getLocationForModule(StandardLocation.SOURCE_OUTPUT, mod); + } JavaFileObject jfo = this._fileManager.getJavaFileForOutput(location, name.toString(), JavaFileObject.Kind.SOURCE, null); URI uri = jfo.toUri(); if (this._createdFiles.contains(uri)) { @@ -155,7 +185,7 @@ public JavaFileObject createSourceFile(CharSequence name, this._createdFiles.add(uri); // hook the file object's writers to create compilation unit and add to addedUnits() - return new HookedJavaFileObject(jfo, jfo.getName(), name.toString(), this); + return new HookedJavaFileObject(jfo, jfo.getName(), name.toString(), this, mod, this._encoding); } /* (non-Javadoc) diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/dispatch/BatchProcessingEnvImpl.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/dispatch/BatchProcessingEnvImpl.java index df97cb6e407..b0f56b9ce37 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/dispatch/BatchProcessingEnvImpl.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/dispatch/BatchProcessingEnvImpl.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2006, 2023 BEA Systems, Inc. + * Copyright (c) 2006, 2024 BEA Systems, Inc. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -89,7 +89,7 @@ public BatchProcessingEnvImpl(BaseAnnotationProcessorManager dispatchManager, Ma this._fileManager = manager; } this._processorOptions = Collections.unmodifiableMap(parseProcessorOptions(commandLineArguments)); - this._filer = new BatchFilerImpl(this._dispatchManager, this); + this._filer = new BatchFilerImpl(this._dispatchManager, this, this._compilerOwner); this._messager = new BatchMessagerImpl(this, this._compilerOwner); } diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/dispatch/HookedJavaFileObject.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/dispatch/HookedJavaFileObject.java index d55d8d39cd6..0a0a83c96de 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/dispatch/HookedJavaFileObject.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/dispatch/HookedJavaFileObject.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2006, 2023 BEA Systems, Inc. and others + * Copyright (c) 2006, 2024 BEA Systems, Inc. and others * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -27,6 +27,7 @@ import org.eclipse.jdt.internal.compiler.env.IBinaryType; import org.eclipse.jdt.internal.compiler.lookup.BinaryTypeBinding; import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding; +import org.eclipse.jdt.internal.compiler.problem.AbortCompilationUnit; /** * A delegating JavaFileObject that hooks the close() methods of the Writer @@ -200,11 +201,17 @@ public String toString() { private final String _typeName; - public HookedJavaFileObject(JavaFileObject fileObject, String fileName, String typeName, BatchFilerImpl filer) { + private final String _moduleName; + + private final String _encoding; + + public HookedJavaFileObject(JavaFileObject fileObject, String fileName, String typeName, BatchFilerImpl filer, String module, String encoding) { super(fileObject); this._filer = filer; this._fileName = fileName; this._typeName = typeName; + this._moduleName = module; + this._encoding = encoding; } @SuppressWarnings("resource") // ForwardingOutputStream forwards close() too @@ -222,11 +229,21 @@ public Writer openWriter() throws IOException { protected void closed() { if (!this._closed) { this._closed = true; - //TODO: support encoding switch(this.getKind()) { case SOURCE : - CompilationUnit unit = new CompilationUnit(null, this._fileName, null /* encoding */, null, this._filer._env.shouldIgnoreOptionalProblems(this._fileName.toCharArray()), null); - this._filer.addNewUnit(unit); + try { + CompilationUnit unit = new CompilationUnit( + getCharContent(false).toString().toCharArray(), + this._fileName, + this._encoding, + null /* destination path */, + this._filer._env.shouldIgnoreOptionalProblems(this._fileName.toCharArray()), + this._moduleName); + this._filer.addNewUnit(unit); + } catch (IOException e) { + e.printStackTrace(); + throw new AbortCompilationUnit(null, e, this._encoding); + } break; case CLASS : IBinaryType binaryType = null; diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/batch/ClasspathJsr199.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/batch/ClasspathJsr199.java index 2e88aec470b..08dde56872f 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/batch/ClasspathJsr199.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/batch/ClasspathJsr199.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2015, 2018 IBM Corporation and others. + * Copyright (c) 2015, 2024 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -18,6 +18,7 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -25,47 +26,68 @@ import java.util.Iterator; import java.util.List; import java.util.Set; +import java.util.function.Supplier; import javax.tools.JavaFileManager; import javax.tools.JavaFileObject; +import javax.tools.JavaFileObject.Kind; +import javax.tools.StandardLocation; import org.eclipse.jdt.core.compiler.CharOperation; +import org.eclipse.jdt.internal.compiler.CompilationResult; +import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; import org.eclipse.jdt.internal.compiler.batch.FileSystem.Classpath; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader; import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException; import org.eclipse.jdt.internal.compiler.env.IModule; import org.eclipse.jdt.internal.compiler.env.NameEnvironmentAnswer; +import org.eclipse.jdt.internal.compiler.parser.Parser; +import org.eclipse.jdt.internal.compiler.tool.ModuleLocationHandler.LocationWrapper; +import org.eclipse.jdt.internal.compiler.util.JRTUtil; @SuppressWarnings({ "rawtypes", "unchecked" }) public class ClasspathJsr199 extends ClasspathLocation { - private static final Set fileTypes = new HashSet<>(); - static { - fileTypes.add(JavaFileObject.Kind.CLASS); - } + private final static String NO_PATH = ""; //$NON-NLS-1$ + private final Set fileTypes; private final JavaFileManager fileManager; private final JavaFileManager.Location location; private Classpath jrt; + private Supplier parserSupplier; + private String encoding; + + /** + * FileSystem.internalFindClass() detects request for initial files by filename, + * which is not suitable for JavaFileObjects with custom URI format. + * Thus we need to compare JavaFileObjects, too. + */ + private Set initialJavaFileObjects; public ClasspathJsr199(JavaFileManager file, JavaFileManager.Location location) { super(null, null); this.fileManager = file; this.location = location; + this.fileTypes = location == StandardLocation.SOURCE_PATH + ? Collections.singleton(JavaFileObject.Kind.SOURCE) + : Collections.singleton(JavaFileObject.Kind.CLASS); } public ClasspathJsr199(Classpath jrt, JavaFileManager file, JavaFileManager.Location location) { - super(null, null); - this.fileManager = file; + this(file, location); this.jrt = jrt; - this.location = location; } /* * Maintain two separate constructors to avoid this being constructed with any other kind of classpath * (other than ClasspathJrt and ClasspathJep249 */ public ClasspathJsr199(ClasspathJep247 older, JavaFileManager file, JavaFileManager.Location location) { - super(null, null); - this.fileManager = file; + this(file, location); this.jrt = older; - this.location = location; + } + + /** Constructor for mapping SOURCE_PATH to ClasspathLocation. */ + public ClasspathJsr199(JavaFileManager fileManager, JavaFileManager.Location location, Set initialJavaFileObjects, Supplier parserSupplier) { + this(fileManager, location); + this.initialJavaFileObjects = initialJavaFileObjects; + this.parserSupplier = parserSupplier; } @Override @@ -89,7 +111,7 @@ public NameEnvironmentAnswer findClass(char[] typeName, String qualifiedPackageN String className = lastDot < 0 ? qualifiedBinaryFileName : qualifiedBinaryFileName.substring(0, lastDot); JavaFileObject jfo = null; try { - jfo = this.fileManager.getJavaFileForInput(this.location, className, JavaFileObject.Kind.CLASS); + jfo = this.fileManager.getJavaFileForInput(this.location, className, this.fileTypes.iterator().next()); } catch (IOException e) { // treat as if class file is missing } @@ -97,11 +119,18 @@ public NameEnvironmentAnswer findClass(char[] typeName, String qualifiedPackageN if (jfo == null) return null; // most common case - try (InputStream inputStream = jfo.openInputStream()) { - ClassFileReader reader = ClassFileReader.read(inputStream.readAllBytes(), qualifiedBinaryFileName); + char[] answerModule = this.module != null ? this.module.name() : null; + if (jfo.getKind() == Kind.CLASS) { + ClassFileReader reader = readJavaClass(jfo, qualifiedBinaryFileName); if (reader != null) { - return new NameEnvironmentAnswer(reader, fetchAccessRestriction(qualifiedBinaryFileName)); + return new NameEnvironmentAnswer(reader, fetchAccessRestriction(qualifiedBinaryFileName), answerModule); } + } else { + if (this.initialJavaFileObjects != null && this.initialJavaFileObjects.contains(jfo)) + return null; // refuse to re-add an initial file (possibly via a wrong module?) + CompilationUnit cu = readCompilationUnit(jfo, this.encoding); + cu.module = answerModule; + return new NameEnvironmentAnswer(cu, fetchAccessRestriction(qualifiedBinaryFileName), answerModule); } } catch (ClassFormatException e) { // treat as if class file is missing @@ -121,7 +150,7 @@ public char[][][] findTypeNames(String aQualifiedPackageName, String moduleName) Iterable files = null; try { - files = this.fileManager.list(this.location, qualifiedPackageName, fileTypes, false); + files = this.fileManager.list(this.location, qualifiedPackageName, this.fileTypes, false); } catch (IOException e) { // treat as if empty } @@ -156,12 +185,37 @@ public char[][][] findTypeNames(String aQualifiedPackageName, String moduleName) public void initialize() throws IOException { if (this.jrt != null) { this.jrt.initialize(); + } else if (this.location instanceof LocationWrapper wrapper) { + for (Path locPath : wrapper.getPaths()) { + File file = locPath.toFile(); + IModule mod = ModuleFinder.scanForModule(this, file, null, true, null); + if (mod != null) + return; + } + } else { + if (this.location == StandardLocation.SOURCE_PATH) { + for (JavaFileObject javaFileObject : this.fileManager.list(this.location, NO_PATH, Collections.singleton(JavaFileObject.Kind.SOURCE), false)) { + if (javaFileObject.getName().equals(IModule.MODULE_INFO_JAVA)) { + this.module = ClasspathJsr199.extractModuleFromFileObject(javaFileObject, this.parserSupplier, this, this.encoding); + return; + } + } + } else { + for (JavaFileObject javaFileObject : this.fileManager.list(this.location, NO_PATH, Collections.singleton(JavaFileObject.Kind.CLASS), false)) { + if (javaFileObject.getName().equals(IModule.MODULE_INFO_CLASS)) { + this.module = ClasspathJsr199.extractModuleFromFileObject(javaFileObject, null, this, this.encoding); + return; + } + } + } } } @Override public void acceptModule(IModule mod) { - // do nothing + if (this.jrt != null) + return; // do nothing + this.module = mod; } @Override @@ -174,7 +228,7 @@ public char[][] getModulesDeclaringPackage(String aQualifiedPackageName, String boolean result = false; try { - Iterable files = this.fileManager.list(this.location, qualifiedPackageName, fileTypes, false); + Iterable files = this.fileManager.list(this.location, qualifiedPackageName, this.fileTypes, false); Iterator f = files.iterator(); // if there is any content, assume a package if (f.hasNext()) { @@ -182,7 +236,7 @@ public char[][] getModulesDeclaringPackage(String aQualifiedPackageName, String } else { // I hate to do this since it is expensive and will throw off garbage // but can't think of an alternative now - files = this.fileManager.list(this.location, qualifiedPackageName, fileTypes, true); + files = this.fileManager.list(this.location, qualifiedPackageName, this.fileTypes, true); f = files.iterator(); // if there is any content, assume a package if (f.hasNext()) { @@ -195,6 +249,29 @@ public char[][] getModulesDeclaringPackage(String aQualifiedPackageName, String return singletonModuleNameIf(result); } + @Override + public char[][] listPackages() { + Set packageNames = new HashSet<>(); + try { + for (JavaFileObject fileObject : this.fileManager.list(this.location, NO_PATH, this.fileTypes, true)) { + String name = fileObject.getName(); + int lastSlash = name.lastIndexOf('/'); + if (lastSlash != -1) { + packageNames.add(name.substring(0, lastSlash).replace('/', '.')); + } + } + char[][] result = new char[packageNames.size()][]; + int i = 0; + for (String s : packageNames) { + result[i++] = s.toCharArray(); + } + return result; + } catch (IOException e) { + // treat as if empty + } + return CharOperation.NO_CHAR_CHAR; + } + @Override public boolean hasCompilationUnit(String qualifiedPackageName, String moduleName) { if (this.jrt != null) @@ -250,6 +327,9 @@ public boolean hasAnnotationFileFor(String qualifiedTypeName) { public Collection getModuleNames(Collection limitModules) { if (this.jrt != null) return this.jrt.getModuleNames(limitModules); + if (this.module != null) { + return Collections.singletonList(String.valueOf(this.module.name())); + } return Collections.emptyList(); } @@ -269,10 +349,68 @@ public IModule getModule(char[] name) { return super.getModule(name); } + @Override + public IModule getModule() { + return this.module; + } + @Override public NameEnvironmentAnswer findClass(char[] typeName, String qualifiedPackageName, String moduleName, String qualifiedBinaryFileName) { // return findClass(typeName, qualifiedPackageName, moduleName, qualifiedBinaryFileName, false); } + + public static ClassFileReader readJavaClass(JavaFileObject jfo, String name) throws ClassFormatException, IOException { + try (InputStream inputStream = jfo.openInputStream()) { + return ClassFileReader.read(inputStream.readAllBytes(), name); + } + } + + public static CompilationUnit readCompilationUnit(JavaFileObject jfo, String encoding) throws IOException { + return new CompilationUnit(jfo.getCharContent(false).toString().toCharArray(), jfo.getName(), encoding); + } + + public static IModule extractModuleFromFileObject(JavaFileObject javaFileObject, Supplier parserSupplier, Classpath pathEntry, String encoding) { + try { + switch (javaFileObject.getKind()) { + case SOURCE: + return extractModuleFromSource(javaFileObject, parserSupplier.get(), pathEntry, encoding); + case CLASS: + return extractModuleFromClass(javaFileObject, pathEntry); + default: + throw new IllegalArgumentException("Unexpected kind "+javaFileObject.getKind()); //$NON-NLS-1$ + } + } catch (IOException e) { + String error = "Failed to read module from " + pathEntry; //$NON-NLS-1$ + if (JRTUtil.PROPAGATE_IO_ERRORS) { + throw new IllegalStateException(error, e); + } else { + System.err.println(error); + e.printStackTrace(); + } + } catch (ClassFormatException e) { + e.printStackTrace(); + } + return null; + } + static IModule extractModuleFromSource(JavaFileObject javaFileObject, Parser parser, Classpath pathEntry, String encoding) throws IOException { + CompilationUnit cu = readCompilationUnit(javaFileObject, encoding); + CompilationResult compilationResult = new CompilationResult(cu, 0, 1, 10); + CompilationUnitDeclaration unit = parser.parse(cu, compilationResult); + if (unit.isModuleInfo() && unit.moduleDeclaration != null) { + cu.module = unit.moduleDeclaration.moduleName; + return new BasicModule(unit.moduleDeclaration, pathEntry); + } + return null; + } + static IModule extractModuleFromClass(JavaFileObject javaFileObject, Classpath pathEntry) throws ClassFormatException, IOException{ + ClassFileReader reader = readJavaClass(javaFileObject, IModule.MODULE_INFO_CLASS); + if (reader != null) { + IModule module = reader.getModuleDeclaration(); + if (module != null) + return reader.getModuleDeclaration(); + } + return null; + } } diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/batch/Main.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/batch/Main.java index 0d34b5c95e3..e69ba8127f7 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/batch/Main.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/batch/Main.java @@ -45,6 +45,7 @@ import java.util.*; import java.util.Map.Entry; import java.util.function.Function; +import java.util.function.Supplier; import java.util.stream.IntStream; import org.eclipse.jdt.core.compiler.CategorizedProblem; import org.eclipse.jdt.core.compiler.CharOperation; @@ -3082,11 +3083,11 @@ private String validateModuleVersion(String versionString) { return versionString; } -private Parser getNewParser() { +public Parser getNewParser() { return new Parser(new ProblemReporter(getHandlingPolicy(), new CompilerOptions(this.options), getProblemFactory()), false); } -private IModule extractModuleDesc(String fileName) { +private IModule extractModuleDesc(String fileName, Supplier cuSupplier) { IModule mod = null; if (fileName.toLowerCase().endsWith(IModule.MODULE_INFO_JAVA)) { // this.options may not be completely populated yet, and definitely not @@ -3096,7 +3097,7 @@ private IModule extractModuleDesc(String fileName) { Parser parser = new Parser(new ProblemReporter(getHandlingPolicy(), new CompilerOptions(opts), getProblemFactory()), false); - ICompilationUnit cu = new CompilationUnit(null, fileName, null); + ICompilationUnit cu = cuSupplier.get(); CompilationResult compilationResult = new CompilationResult(cu, 0, 1, 10); CompilationUnitDeclaration unit = parser.parse(cu, compilationResult); if (unit.isModuleInfo() && unit.moduleDeclaration != null) { @@ -3553,8 +3554,11 @@ private void handleSingleModuleCompilation() { return; } IModule singleMod = null; - for (String filename : this.filenames) { - IModule mod = extractModuleDesc(filename); + String[] names = this.filenames; + for (int i = 0; i < names.length; i++) { + String filename = names[i]; + int idx = i; // idx is used in EclipseCompilerImpl.createCompilationUnit() + IModule mod = extractModuleDesc(filename, () -> createCompilationUnit(idx, filename)); if (mod != null) { if (singleMod == null) { singleMod = mod; @@ -3571,6 +3575,20 @@ private void handleSingleModuleCompilation() { this.module = singleMod; } } +// overridable hook +protected CompilationUnit createCompilationUnit(int idx, String filename) { + return new CompilationUnit(null, filename, null); +} +/* + * External API + */ +public String getDefaultEncoding() { + if (this.compilerOptions != null) + return this.compilerOptions.defaultEncoding; + if (this.options != null) + return new CompilerOptions(this.options).defaultEncoding; + return null; +} /* * External API */ diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/batch/ModuleFinder.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/batch/ModuleFinder.java index 6a36f108ea9..ff875714692 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/batch/ModuleFinder.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/batch/ModuleFinder.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2016, 2020 IBM Corporation. + * Copyright (c) 2016, 2024 IBM Corporation. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -40,7 +40,7 @@ public class ModuleFinder { public static List findModules(File f, String destinationPath, Parser parser, Map options, boolean isModulepath, String release) { List collector = new ArrayList<>(); - scanForModules(destinationPath, parser, options, isModulepath, false, collector, f, release); + scanForModules(destinationPath, parser, options, isModulepath, !f.isDirectory(), collector, f, release); return collector; } @@ -97,7 +97,8 @@ public boolean accept(File dir, String name) { module = ModuleFinder.extractModuleFromClass(new File(file, fileName), modulePath); break; case IModule.MODULE_INFO_JAVA: - module = ModuleFinder.extractModuleFromSource(new File(file, fileName), parser, modulePath); + if (parser != null) + module = ModuleFinder.extractModuleFromSource(new File(file, fileName), parser, modulePath); if (module == null) return null; String modName = new String(module.name()); diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java index 50a50eb0cd9..50ee30d6e68 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java @@ -11584,9 +11584,10 @@ public void invalidOpensStatement(OpensStatement statement, ModuleDeclaration mo statement.declarationSourceStart, statement.declarationSourceEnd); } public void invalidPackageReference(int problem, PackageVisibilityStatement ref) { + String[] arguments = new String[] { CharOperation.charToString(ref.pkgName) }; this.handle(problem, - NoArgument, - new String[] { CharOperation.charToString(ref.pkgName) }, + arguments, + arguments, ref.computeSeverity(problem), ref.pkgRef.sourceStart, ref.pkgRef.sourceEnd); diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/tool/EclipseCompiler.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/tool/EclipseCompiler.java index 0f7241f6fe2..80e195e1dcb 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/tool/EclipseCompiler.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/tool/EclipseCompiler.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2006, 2018 IBM Corporation and others. + * Copyright (c) 2006, 2024 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -112,7 +112,11 @@ public CompilationTask getTask(Writer out, JavaFileManager fileManager, Diagnost eclipseCompiler.initialize(writerOut, writerErr, false, null/*options*/, null/*progress*/); } final EclipseCompilerImpl eclipseCompiler2 = new EclipseCompilerImpl(writerOut, writerErr, false); - eclipseCompiler2.compilationUnits = compilationUnits; + eclipseCompiler2.compilationUnits = new ArrayList<>(); + if (compilationUnits != null) { + for (JavaFileObject javaFileObject : compilationUnits) + eclipseCompiler2.compilationUnits.add(javaFileObject); + } eclipseCompiler2.diagnosticListener = someDiagnosticListener; if (fileManager != null) { eclipseCompiler2.fileManager = fileManager; @@ -135,21 +139,19 @@ public CompilationTask getTask(Writer out, JavaFileManager fileManager, Diagnost } } - if (compilationUnits != null) { - for (JavaFileObject javaFileObject : compilationUnits) { - // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6419926 - // compells us to check that the returned URIs are absolute, - // which they happen not to be for the default compiler on some - // unices - URI uri = javaFileObject.toUri(); - if (!uri.isAbsolute()) { - uri = URI.create("file://" + uri.toString()); //$NON-NLS-1$ - } - if (uri.getScheme().equals("file")) { //$NON-NLS-1$ - allOptions.add(new File(uri).getAbsolutePath()); - } else { - allOptions.add(uri.toString()); - } + for (JavaFileObject javaFileObject : eclipseCompiler2.compilationUnits) { + // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6419926 + // compells us to check that the returned URIs are absolute, + // which they happen not to be for the default compiler on some + // unices + URI uri = javaFileObject.toUri(); + if (!uri.isAbsolute()) { + uri = URI.create("file://" + uri.toString()); //$NON-NLS-1$ + } + if (uri.getScheme().equals("file")) { //$NON-NLS-1$ + allOptions.add(new File(uri).getAbsolutePath()); + } else { + allOptions.add(uri.toString()); } } diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/tool/EclipseCompilerImpl.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/tool/EclipseCompilerImpl.java index 3ebe575228d..417a34f62ac 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/tool/EclipseCompilerImpl.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/tool/EclipseCompilerImpl.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2007, 2021 IBM Corporation and others. + * Copyright (c) 2007, 2024 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -28,14 +28,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Optional; +import java.util.*; import java.util.stream.Stream; import javax.annotation.processing.Processor; import javax.tools.Diagnostic; @@ -76,7 +69,8 @@ public class EclipseCompilerImpl extends Main { private static final String RELEASE_FILE = "release"; //$NON-NLS-1$ private static final String JAVA_VERSION = "JAVA_VERSION"; //$NON-NLS-1$ private HashMap javaFileObjectMap; - Iterable compilationUnits; + /** Corresponds to filenames in plain batch compilation, indices are synced also with modNames. */ + List compilationUnits; public JavaFileManager fileManager; protected Processor[] processors; public DiagnosticListener diagnosticListener; @@ -171,24 +165,20 @@ public CompilationUnit[] getCompilationUnits() { throw new IllegalArgumentException(this.bind("unit.missing", name)); //$NON-NLS-1$ } - CompilationUnit cu = new CompilationUnit(null, + String encoding = getDefaultEncoding(); + try { + CompilationUnit cu = new CompilationUnit(javaFileObject.getCharContent(false).toString().toCharArray(), name, - null, + encoding, this.destinationPaths[i], - shouldIgnoreOptionalProblems(this.ignoreOptionalProblemsFromFolders, name.toCharArray()), this.modNames[i]) { - - @Override - public char[] getContents() { - try { - return javaFileObject.getCharContent(true).toString().toCharArray(); - } catch(IOException e) { - e.printStackTrace(); - throw new AbortCompilationUnit(null, e, null); - } - } - }; + shouldIgnoreOptionalProblems(this.ignoreOptionalProblemsFromFolders, name.toCharArray()), + this.modNames[i]); units.add(cu); this.javaFileObjectMap.put(cu, javaFileObject); + } catch (IOException e) { + e.printStackTrace(); + throw new AbortCompilationUnit(null, e, encoding); + } } i++; } @@ -715,7 +705,7 @@ protected void handleLocations() { } else if (javaFileManager != null) { Classpath classpath = null; if (this.fileManager.hasLocation(StandardLocation.SOURCE_PATH)) { - classpath = new ClasspathJsr199(this.fileManager, StandardLocation.SOURCE_PATH); + classpath = new ClasspathJsr199(this.fileManager, StandardLocation.SOURCE_PATH, new HashSet<>(this.compilationUnits), this::getNewParser); fileSystemClasspaths.add(classpath); } // Add the locations to search for in specific order @@ -727,12 +717,28 @@ protected void handleLocations() { fileSystemClasspaths.add(classpath); } if (this.fileManager.hasLocation(StandardLocation.MODULE_SOURCE_PATH)) { - classpath = new ClasspathJsr199(this.fileManager, StandardLocation.MODULE_SOURCE_PATH); - fileSystemClasspaths.add(classpath); + try { + for (Set locs: this.fileManager.listLocationsForModules(StandardLocation.MODULE_SOURCE_PATH)) { + for (Location loc : locs) { + classpath = new ClasspathJsr199(this.fileManager, loc, new HashSet<>(this.compilationUnits), this::getNewParser); + fileSystemClasspaths.add(classpath); + } + } + } catch (IOException e) { + this.logger.logException(e); + } } if (this.fileManager.hasLocation(StandardLocation.MODULE_PATH)) { - classpath = new ClasspathJsr199(this.fileManager, StandardLocation.MODULE_PATH); - fileSystemClasspaths.add(classpath); + try { + for (Set locs: this.fileManager.listLocationsForModules(StandardLocation.MODULE_PATH)) { + for (Location loc : locs) { + classpath = new ClasspathJsr199(this.fileManager, loc); + fileSystemClasspaths.add(classpath); + } + } + } catch (IOException e) { + this.logger.logException(e); + } } classpath = new ClasspathJsr199(this.fileManager, StandardLocation.CLASS_PATH); fileSystemClasspaths.add(classpath); @@ -973,4 +979,23 @@ public String getMarkerType() { } } + + @Override + protected CompilationUnit createCompilationUnit(int idx, String filename) { + // NOTE: use of idx relies on same order in these members: + // + from Main: filenames, modNames + // + here: compilationUnits (see comment at declaration) + // I.e., instead of using filenames[idx] we use compilationUnits.get(idx): + if (this.compilationUnits != null && idx < this.compilationUnits.size()) { + JavaFileObject javaFileObject = this.compilationUnits.get(idx); + if (filename.endsWith(javaFileObject.getName())) { + try { + return ClasspathJsr199.readCompilationUnit(javaFileObject, getDefaultEncoding()); + } catch (IOException e) { + // nop + } + } + } + return null; + } } diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/tool/EclipseFileManager.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/tool/EclipseFileManager.java index 2815046d463..448a7f96750 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/tool/EclipseFileManager.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/tool/EclipseFileManager.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2006, 2022 IBM Corporation and others. + * Copyright (c) 2006, 2024 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -25,6 +25,7 @@ import java.nio.file.Paths; import java.text.MessageFormat; import java.util.*; +import java.util.function.Function; import javax.lang.model.SourceVersion; import javax.tools.FileObject; import javax.tools.JavaFileObject; @@ -151,13 +152,14 @@ private void collectAllMatchingFiles(Location location, File file, String normal Archive archive = this.getArchive(file); if (archive != Archive.UNKNOWN_ARCHIVE) { String key = normalizedPackageName; + boolean findAll = key.length() == 0; if (!normalizedPackageName.endsWith("/")) {//$NON-NLS-1$ key += '/'; } // we have an archive file if (recurse) { for (String packageName : archive.allPackages()) { - if (packageName.startsWith(key)) { + if (findAll || packageName.startsWith(key)) { List types = archive.getTypes(packageName); if (types != null) { for (String[] entry : types) { @@ -1021,6 +1023,10 @@ private boolean isRunningJvm9() { */ @Override public void setLocation(Location location, Iterable files) throws IOException { + internalSetLocation(location, files); + initModules(location, files, Function.identity()); + } + private void internalSetLocation(Location location, Iterable files) { if (location.isOutputLocation() && files != null) { // output location int count = 0; @@ -1406,9 +1412,11 @@ public Iterable getLocationAsPaths(Location location) { @Override public void setLocationFromPaths(Location location, Collection paths) throws IOException { - setLocation(location, getFiles(paths)); + internalSetLocation(location, getFiles(paths)); + initModules(location, paths, Path::toFile); + } + private

void initModules(Location location, Iterable paths, Function toFile) throws IOException { if (location == StandardLocation.MODULE_PATH || location == StandardLocation.MODULE_SOURCE_PATH) { - // FIXME: same for module source path? Map options = new HashMap<>(); // FIXME: Find a way to get the options from the EclipseCompiler and pass it to the parser. String latest = CompilerOptions.getLatestVersion(); @@ -1421,15 +1429,19 @@ public void setLocationFromPaths(Location location, Collection p DefaultErrorHandlingPolicies.proceedWithAllProblems(), compilerOptions, new DefaultProblemFactory()); - for (Path path : paths) { - List mp = ModuleFinder.findModules(path.toFile(), null, - new Parser(problemReporter, true), null, true, this.releaseVersion); - for (Classpath cp : mp) { - Collection moduleNames = cp.getModuleNames(null); - for (String string : moduleNames) { - Path p = Paths.get(cp.getPath()); - setLocationForModule(location, string, Collections.singletonList(p)); + for (P path : paths) { + try { + List mp = ModuleFinder.findModules(toFile.apply(path), null, + new Parser(problemReporter, true), null, true, this.releaseVersion); + for (Classpath cp : mp) { + Collection moduleNames = cp.getModuleNames(null); + for (String string : moduleNames) { + Path p = Paths.get(cp.getPath()); + setLocationForModule(location, string, Collections.singletonList(p)); + } } + } catch (IllegalArgumentException iae) { // e.g., from ModuleFinder.scanForModule(Classpath, File, Parser, boolean, String) + // ignore } } }