Bug 527260 - [Java 9] need an equivalent option to the javac --release
option
Change-Id: I8e319b20a18cf18c43df29a3ed07a3525b4f9fe8
Signed-off-by: Jay Arthanareeswaran <jarthana@in.ibm.com>
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 588a928..3e65152 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
@@ -10,32 +10,50 @@
*******************************************************************************/
package org.eclipse.jdt.compiler.tool.tests;
+import java.io.BufferedWriter;
+import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
+import java.io.FileWriter;
import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.file.Path;
import java.nio.file.Paths;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
import java.util.Locale;
import java.util.ServiceLoader;
import javax.lang.model.SourceVersion;
+import javax.tools.Diagnostic;
+import javax.tools.FileObject;
+import javax.tools.ForwardingJavaFileManager;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileManager;
+import javax.tools.JavaFileObject;
import javax.tools.JavaFileManager.Location;
+import javax.tools.JavaFileObject.Kind;
import javax.tools.StandardJavaFileManager;
import javax.tools.StandardLocation;
import javax.tools.ToolProvider;
+import javax.tools.JavaCompiler.CompilationTask;
import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.Platform;
+import org.eclipse.jdt.compiler.tool.tests.AbstractCompilerToolTest.CompilerInvocationDiagnosticListener;
+import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
+import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader;
+import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException;
import org.eclipse.jdt.internal.compiler.tool.EclipseCompiler;
import junit.framework.TestCase;
public class CompilerToolJava9Tests extends TestCase {
+ private static final boolean DEBUG = false;
private static final String RESOURCES_DIR = "resources";
private JavaCompiler[] compilers;
private String[] compilerNames;
@@ -153,6 +171,306 @@
}
}
}
+ public void testOptionRelease1() {
+ if (this.isJREBelow9) return;
+ JavaCompiler compiler = this.compilers[1];
+ String tmpFolder = System.getProperty("java.io.tmpdir");
+ File inputFile = new File(tmpFolder, "X.java");
+ BufferedWriter writer = null;
+ try {
+ writer = new BufferedWriter(new FileWriter(inputFile));
+ writer.write(
+ "package p;\n" +
+ "public class X {}");
+ writer.flush();
+ writer.close();
+ } catch (IOException e) {
+ // ignore
+ } finally {
+ if (writer != null) {
+ try {
+ writer.close();
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+ }
+ StandardJavaFileManager manager = compiler.getStandardFileManager(null, Locale.getDefault(), Charset.defaultCharset());
+
+ ForwardingJavaFileManager<StandardJavaFileManager> forwardingJavaFileManager = new ForwardingJavaFileManager<StandardJavaFileManager>(manager) {
+ @Override
+ public FileObject getFileForInput(Location location, String packageName, String relativeName)
+ throws IOException {
+ if (DEBUG) {
+ System.out.println("Create file for input : " + packageName + " " + relativeName + " in location " + location);
+ }
+ return super.getFileForInput(location, packageName, relativeName);
+ }
+ @Override
+ public JavaFileObject getJavaFileForInput(Location location, String className, Kind kind)
+ throws IOException {
+ if (DEBUG) {
+ System.out.println("Create java file for input : " + className + " in location " + location);
+ }
+ return super.getJavaFileForInput(location, className, kind);
+ }
+ @Override
+ public JavaFileObject getJavaFileForOutput(Location location,
+ String className,
+ Kind kind,
+ FileObject sibling) throws IOException {
+
+ if (DEBUG) {
+ System.out.println("Create .class file for " + className + " in location " + location + " with sibling " + sibling.toUri());
+ }
+ JavaFileObject javaFileForOutput = super.getJavaFileForOutput(location, className, kind, sibling);
+ if (DEBUG) {
+ System.out.println(javaFileForOutput.toUri());
+ }
+ return javaFileForOutput;
+ }
+ };
+ // create new list containing input file
+ List<File> files = new ArrayList<File>();
+ files.add(inputFile);
+ Iterable<? extends JavaFileObject> units = manager.getJavaFileObjectsFromFiles(files);
+ StringWriter stringWriter = new StringWriter();
+ PrintWriter printWriter = new PrintWriter(stringWriter);
+
+ List<String> options = new ArrayList<String>();
+ options.add("-d");
+ options.add(tmpFolder);
+ options.add("--release");
+ options.add("8");
+ ByteArrayOutputStream errBuffer = new ByteArrayOutputStream();
+ PrintWriter err = new PrintWriter(errBuffer);
+ CompilerInvocationDiagnosticListener listener = new CompilerInvocationDiagnosticListener(err) {
+ @Override
+ public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
+ JavaFileObject source = diagnostic.getSource();
+ assertNotNull("No source", source);
+ super.report(diagnostic);
+ }
+ };
+ CompilationTask task = compiler.getTask(printWriter, forwardingJavaFileManager, listener, options, null, units);
+ // check the classpath location
+ assertTrue("Has no location CLASS_OUPUT", forwardingJavaFileManager.hasLocation(StandardLocation.CLASS_OUTPUT));
+ Boolean result = task.call();
+ printWriter.flush();
+ printWriter.close();
+ if (!result.booleanValue()) {
+ System.err.println("Compilation failed: " + stringWriter.getBuffer().toString());
+ assertTrue("Compilation failed ", false);
+ }
+ ClassFileReader reader = null;
+ try {
+ reader = ClassFileReader.read(new File(tmpFolder, "p/X.class"), true);
+ } catch (ClassFormatException e) {
+ assertTrue("Should not happen", false);
+ } catch (IOException e) {
+ assertTrue("Should not happen", false);
+ }
+ assertNotNull("No reader", reader);
+ // This needs fix. This test case by design will produce different output every compiler version.
+ assertEquals("Wrong value", ClassFileConstants.JDK1_8, reader.getVersion());
+ // check that the .class file exist for X
+ assertTrue("delete failed", inputFile.delete());
+ }
+ public void testOptionRelease2() {
+ if (this.isJREBelow9) return;
+ JavaCompiler compiler = this.compilers[1];
+ String tmpFolder = System.getProperty("java.io.tmpdir");
+ File inputFile = new File(tmpFolder, "X.java");
+ BufferedWriter writer = null;
+ try {
+ writer = new BufferedWriter(new FileWriter(inputFile));
+ writer.write(
+ "package p;\n" +
+ "public class X {}");
+ writer.flush();
+ writer.close();
+ } catch (IOException e) {
+ // ignore
+ } finally {
+ if (writer != null) {
+ try {
+ writer.close();
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+ }
+ StandardJavaFileManager manager = compiler.getStandardFileManager(null, Locale.getDefault(), Charset.defaultCharset());
+
+ ForwardingJavaFileManager<StandardJavaFileManager> forwardingJavaFileManager = new ForwardingJavaFileManager<StandardJavaFileManager>(manager) {
+ @Override
+ public FileObject getFileForInput(Location location, String packageName, String relativeName)
+ throws IOException {
+ if (DEBUG) {
+ System.out.println("Create file for input : " + packageName + " " + relativeName + " in location " + location);
+ }
+ return super.getFileForInput(location, packageName, relativeName);
+ }
+ @Override
+ public JavaFileObject getJavaFileForInput(Location location, String className, Kind kind)
+ throws IOException {
+ if (DEBUG) {
+ System.out.println("Create java file for input : " + className + " in location " + location);
+ }
+ return super.getJavaFileForInput(location, className, kind);
+ }
+ @Override
+ public JavaFileObject getJavaFileForOutput(Location location,
+ String className,
+ Kind kind,
+ FileObject sibling) throws IOException {
+
+ if (DEBUG) {
+ System.out.println("Create .class file for " + className + " in location " + location + " with sibling " + sibling.toUri());
+ }
+ JavaFileObject javaFileForOutput = super.getJavaFileForOutput(location, className, kind, sibling);
+ if (DEBUG) {
+ System.out.println(javaFileForOutput.toUri());
+ }
+ return javaFileForOutput;
+ }
+ };
+ // create new list containing input file
+ List<File> files = new ArrayList<File>();
+ files.add(inputFile);
+ Iterable<? extends JavaFileObject> units = manager.getJavaFileObjectsFromFiles(files);
+ StringWriter stringWriter = new StringWriter();
+ PrintWriter printWriter = new PrintWriter(stringWriter);
+
+ List<String> options = new ArrayList<String>();
+ options.add("-d");
+ options.add(tmpFolder);
+ options.add("--release");
+ options.add("8");
+ ByteArrayOutputStream errBuffer = new ByteArrayOutputStream();
+ PrintWriter err = new PrintWriter(errBuffer);
+ CompilerInvocationDiagnosticListener listener = new CompilerInvocationDiagnosticListener(err) {
+ @Override
+ public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
+ JavaFileObject source = diagnostic.getSource();
+ assertNotNull("No source", source);
+ super.report(diagnostic);
+ }
+ };
+ CompilationTask task = compiler.getTask(printWriter, forwardingJavaFileManager, listener, options, null, units);
+ // check the classpath location
+ assertTrue("Has no location CLASS_OUPUT", forwardingJavaFileManager.hasLocation(StandardLocation.CLASS_OUTPUT));
+ Boolean result = task.call();
+ printWriter.flush();
+ printWriter.close();
+ if (!result.booleanValue()) {
+ System.err.println("Compilation failed: " + stringWriter.getBuffer().toString());
+ assertTrue("Compilation failed ", false);
+ }
+ ClassFileReader reader = null;
+ try {
+ reader = ClassFileReader.read(new File(tmpFolder, "p/X.class"), true);
+ } catch (ClassFormatException e) {
+ assertTrue("Should not happen", false);
+ } catch (IOException e) {
+ assertTrue("Should not happen", false);
+ }
+ assertNotNull("No reader", reader);
+ // This needs fix. This test case by design will produce different output every compiler version.
+ assertEquals("Wrong value", ClassFileConstants.JDK1_8, reader.getVersion());
+ // check that the .class file exist for X
+ assertTrue("delete failed", inputFile.delete());
+ }
+ public void testOptionRelease3() {
+ if (this.isJREBelow9) return;
+ JavaCompiler compiler = this.compilers[1];
+ String tmpFolder = System.getProperty("java.io.tmpdir");
+ File inputFile = new File(tmpFolder, "X.java");
+ BufferedWriter writer = null;
+ try {
+ writer = new BufferedWriter(new FileWriter(inputFile));
+ writer.write(
+ "package p;\n" +
+ "public class X {}");
+ writer.flush();
+ writer.close();
+ } catch (IOException e) {
+ // ignore
+ } finally {
+ if (writer != null) {
+ try {
+ writer.close();
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+ }
+ StandardJavaFileManager manager = compiler.getStandardFileManager(null, Locale.getDefault(), Charset.defaultCharset());
+
+ ForwardingJavaFileManager<StandardJavaFileManager> forwardingJavaFileManager = new ForwardingJavaFileManager<StandardJavaFileManager>(manager) {
+ @Override
+ public FileObject getFileForInput(Location location, String packageName, String relativeName)
+ throws IOException {
+ if (DEBUG) {
+ System.out.println("Create file for input : " + packageName + " " + relativeName + " in location " + location);
+ }
+ return super.getFileForInput(location, packageName, relativeName);
+ }
+ @Override
+ public JavaFileObject getJavaFileForInput(Location location, String className, Kind kind)
+ throws IOException {
+ if (DEBUG) {
+ System.out.println("Create java file for input : " + className + " in location " + location);
+ }
+ return super.getJavaFileForInput(location, className, kind);
+ }
+ @Override
+ public JavaFileObject getJavaFileForOutput(Location location,
+ String className,
+ Kind kind,
+ FileObject sibling) throws IOException {
+
+ if (DEBUG) {
+ System.out.println("Create .class file for " + className + " in location " + location + " with sibling " + sibling.toUri());
+ }
+ JavaFileObject javaFileForOutput = super.getJavaFileForOutput(location, className, kind, sibling);
+ if (DEBUG) {
+ System.out.println(javaFileForOutput.toUri());
+ }
+ return javaFileForOutput;
+ }
+ };
+ // create new list containing input file
+ List<File> files = new ArrayList<File>();
+ files.add(inputFile);
+ Iterable<? extends JavaFileObject> units = manager.getJavaFileObjectsFromFiles(files);
+ StringWriter stringWriter = new StringWriter();
+ PrintWriter printWriter = new PrintWriter(stringWriter);
+
+ List<String> options = new ArrayList<String>();
+ options.add("-d");
+ options.add(tmpFolder);
+ options.add("--release");
+ options.add("8");
+ options.add("-source");
+ options.add("7");
+ ByteArrayOutputStream errBuffer = new ByteArrayOutputStream();
+ PrintWriter err = new PrintWriter(errBuffer);
+ CompilerInvocationDiagnosticListener listener = new CompilerInvocationDiagnosticListener(err) {
+ @Override
+ public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
+ JavaFileObject source = diagnostic.getSource();
+ assertNotNull("No source", source);
+ super.report(diagnostic);
+ }
+ };
+ try {
+ compiler.getTask(printWriter, forwardingJavaFileManager, listener, options, null, units);
+ fail("compilation didn't fail as expected");
+ } catch(IllegalArgumentException iae) {
+ assertEquals("option -source is not supported when --release is used", iae.getMessage());
+ }
+ }
public void testGetJavaFileObjects() {
if (this.isJREBelow9) return;
}
diff --git a/org.eclipse.jdt.compiler.tool/src/org/eclipse/jdt/internal/compiler/batch/ClasspathJsr199.java b/org.eclipse.jdt.compiler.tool/src/org/eclipse/jdt/internal/compiler/batch/ClasspathJsr199.java
index 4ea2d16..0e11aad 100644
--- a/org.eclipse.jdt.compiler.tool/src/org/eclipse/jdt/internal/compiler/batch/ClasspathJsr199.java
+++ b/org.eclipse.jdt.compiler.tool/src/org/eclipse/jdt/internal/compiler/batch/ClasspathJsr199.java
@@ -27,6 +27,7 @@
import javax.tools.JavaFileObject;
import org.eclipse.jdt.core.compiler.CharOperation;
+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;
@@ -42,19 +43,29 @@
private JavaFileManager fileManager;
private JavaFileManager.Location location;
- private ClasspathJrt jrt;
+ private Classpath jrt;
public ClasspathJsr199(JavaFileManager file, JavaFileManager.Location location) {
super(null, null);
this.fileManager = file;
this.location = location;
}
- public ClasspathJsr199(ClasspathJrt jrt, JavaFileManager file, JavaFileManager.Location location) {
+ public ClasspathJsr199(Classpath jrt, JavaFileManager file, JavaFileManager.Location location) {
super(null, null);
this.fileManager = file;
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.jrt = older;
+ this.location = location;
+ }
@Override
public List fetchLinkedJars(FileSystem.ClasspathSectionProblemReporter problemReporter) {
@@ -142,7 +153,9 @@
@Override
public void initialize() throws IOException {
- // nothing to do
+ if (this.jrt != null) {
+ this.jrt.initialize();
+ }
}
@Override
@@ -195,6 +208,9 @@
} catch (IOException e) {
// ignore
}
+ if (this.jrt != null) {
+ this.jrt.reset();
+ }
}
@Override
diff --git a/org.eclipse.jdt.compiler.tool/src/org/eclipse/jdt/internal/compiler/tool/EclipseCompilerImpl.java b/org.eclipse.jdt.compiler.tool/src/org/eclipse/jdt/internal/compiler/tool/EclipseCompilerImpl.java
index cb5e9e7..b77335f 100644
--- a/org.eclipse.jdt.compiler.tool/src/org/eclipse/jdt/internal/compiler/tool/EclipseCompilerImpl.java
+++ b/org.eclipse.jdt.compiler.tool/src/org/eclipse/jdt/internal/compiler/tool/EclipseCompilerImpl.java
@@ -55,6 +55,7 @@
import org.eclipse.jdt.internal.compiler.batch.FileSystem.Classpath;
import org.eclipse.jdt.internal.compiler.batch.Main;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
+import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
import org.eclipse.jdt.internal.compiler.problem.AbortCompilationUnit;
import org.eclipse.jdt.internal.compiler.problem.DefaultProblem;
@@ -71,7 +72,6 @@
Iterable<? extends JavaFileObject> compilationUnits;
public JavaFileManager fileManager;
protected Processor[] processors;
- // TODO: This is not yet used anywhere
public DiagnosticListener<? super JavaFileObject> diagnosticListener;
public EclipseCompilerImpl(PrintWriter out, PrintWriter err, boolean systemExitWhenFinished) {
@@ -472,6 +472,7 @@
String customEncoding) {
// Sometimes this gets called too early there by losing locations set after that point.
// The code is now moved to handleLocations() which is invoked just before compilation
+ validateClasspathOptions(bootclasspaths, endorsedDirClasspaths, extdirsClasspaths);
}
protected void handleLocations() {
@@ -531,9 +532,20 @@
}
} else if (javaFileManager != null) {
File javaHome = Util.getJavaHome();
- if (Util.getJDKLevel(javaHome) >= ClassFileConstants.JDK9) {
- ClasspathJrt jrt = (ClasspathJrt) FileSystem.getJrtClasspath(javaHome.toString(), null, null, null);
- Classpath classpath = new ClasspathJsr199(jrt, this.fileManager, StandardLocation.PLATFORM_CLASS_PATH);
+ long jdkLevel = Util.getJDKLevel(javaHome);
+ if (jdkLevel >= ClassFileConstants.JDK9) {
+ Classpath system = null;
+ if (this.releaseVersion != null && this.complianceLevel < jdkLevel) {
+ String versionFromJdkLevel = CompilerOptions.versionFromJdkLevel(this.complianceLevel);
+ if (versionFromJdkLevel.length() >= 3) {
+ versionFromJdkLevel = versionFromJdkLevel.substring(2);
+ }
+ // TODO: Revisit for access rules
+ system = FileSystem.getOlderSystemRelease(javaHome.getAbsolutePath(), versionFromJdkLevel, null);
+ } else {
+ system = FileSystem.getJrtClasspath(javaHome.toString(), null, null, null);
+ }
+ Classpath classpath = new ClasspathJsr199(system, this.fileManager, StandardLocation.PLATFORM_CLASS_PATH);
fileSystemClasspaths.add(classpath);
} else {
Classpath classpath = new ClasspathJsr199(this.fileManager, StandardLocation.PLATFORM_CLASS_PATH);
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest.java
index 1b7c6e6..206c2bc 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest.java
@@ -44,6 +44,8 @@
import java.util.Iterator;
import java.util.List;
+import javax.lang.model.SourceVersion;
+
import junit.framework.Test;
import org.eclipse.jdt.core.JavaCore;
@@ -630,7 +632,21 @@
" specify where to find source files for multiple modules\n" +
" -p --module-path <directories separated by " + File.pathSeparator + ">\n" +
" specify where to find application modules\n" +
- " --system <jdk> Override location of system modules \n" +
+ " --system <jdk> Override location of system modules\n" +
+ " --add-exports <module>/<package>=<other-module>(,<other-module>)*\n" +
+ " specify additional package exports clauses to the\n" +
+ " given modules\n" +
+ " --add-reads <module>=<other-module>(,<other-module>)*\n" +
+ " specify additional modules to be considered as required\n" +
+ " by given modules\n" +
+ " --add-modules <module>(,<module>)*\n" +
+ " specify the additional module names that should be\n" +
+ " resolved to be root modules\n" +
+ " --limit-modules <module>(,<module>)*\n" +
+ " specify the observable module names\n" +
+ " --release <release>\n" +
+ " compile for a specific VM version\n" +
+ " \n" +
" Compliance options:\n" +
" -1.3 use 1.3 compliance (-source 1.3 -target 1.1)\n" +
" -1.4 + use 1.4 compliance (-source 1.3 -target 1.2)\n" +
@@ -13040,4 +13056,25 @@
"",
true);
}
+public void testReleaseOption() throws Exception {
+ try {
+ SourceVersion valueOf = SourceVersion.valueOf("RELEASE_9");
+ if (valueOf != null)
+ return;
+ } catch(IllegalArgumentException iae) {
+ // Ignore
+ }
+ this.runNegativeTest(
+ new String[] {
+ "X.java",
+ "/** */\n" +
+ "public class X {\n" +
+ "}",
+ },
+ "\"" + OUTPUT_DIR + File.separator + "X.java\""
+ + " --release 8 -d \"" + OUTPUT_DIR + "\"",
+ "",
+ "option --release is supported only when run with JDK 9 or above\n",
+ true);
+}
}
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/ModuleCompilationTests.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/ModuleCompilationTests.java
index db94630..95fc5da 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/ModuleCompilationTests.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/ModuleCompilationTests.java
@@ -3661,4 +3661,717 @@
false,
OUTPUT_DIR + File.separator + out);
}
+ /*
+ * Test that when module-info is the only file being compiled, the class is still
+ * generated inside the module's sub folder.
+ */
+ public void testBug500170a() {
+ File outputDirectory = new File(OUTPUT_DIR);
+ Util.flushDirectoryContent(outputDirectory);
+ String out = "bin";
+ String directory = OUTPUT_DIR + File.separator + "src";
+ String moduleLoc = directory + File.separator + "mod.one";
+ List<String> files = new ArrayList<>();
+ writeFileCollecting(files, moduleLoc, "module-info.java",
+ "module mod.one { \n" +
+ " requires java.sql;\n" +
+ "}");
+ StringBuffer buffer = new StringBuffer();
+ buffer.append("-d " + OUTPUT_DIR + File.separator + out )
+ .append(" -9 ")
+ .append(" -classpath \"")
+ .append(Util.getJavaClassLibsAsString())
+ .append("\" ")
+ .append(" --module-source-path " + "\"" + directory + "\" ")
+ .append(moduleLoc + File.separator + "module-info.java");
+
+ Set<String> classFiles = runConformModuleTest(
+ new String[] {
+ "mod.one/module-info.java",
+ "module mod.one { \n" +
+ " requires java.sql;\n" +
+ "}"
+ },
+ buffer.toString(),
+ "",
+ "",
+ false);
+ String fileName = OUTPUT_DIR + File.separator + out + File.separator + "mod.one" + File.separator + "module-info.class";
+ assertClassFile("Missing modul-info.class: " + fileName, fileName, classFiles);
+ }
+ /*
+ * Test that no NPE is thrown when the module-info is compiled at a level below 9
+ */
+ public void testBug500170b() {
+ File outputDirectory = new File(OUTPUT_DIR);
+ Util.flushDirectoryContent(outputDirectory);
+ String out = "bin";
+ String directory = OUTPUT_DIR + File.separator + "src";
+ String moduleLoc = directory + File.separator + "mod.one";
+ List<String> files = new ArrayList<>();
+ writeFileCollecting(files, moduleLoc, "module-info.java",
+ "module mod.one { \n" +
+ " requires java.sql;\n" +
+ "}");
+ StringBuffer buffer = new StringBuffer();
+ buffer.append("-d " + OUTPUT_DIR + File.separator + out )
+ .append(" -classpath \"")
+ .append(Util.getJavaClassLibsAsString())
+ .append("\" ")
+ .append(" --module-source-path " + "\"" + directory + "\"");
+
+ runNegativeModuleTest(files,
+ buffer,
+ "",
+ "----------\n" +
+ "1. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/src/mod.one/module-info.java (at line 1)\n" +
+ " module mod.one { \n" +
+ " ^^^^^^\n" +
+ "Syntax error on token \"module\", package expected\n" +
+ "----------\n" +
+ "2. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/src/mod.one/module-info.java (at line 1)\n" +
+ " module mod.one { \n" +
+ " ^^^^^^^^^^^^^^\n" +
+ "Syntax error on token(s), misplaced construct(s)\n" +
+ "----------\n" +
+ "3. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/src/mod.one/module-info.java (at line 2)\n" +
+ " requires java.sql;\n" +
+ " ^\n" +
+ "Syntax error on token \".\", , expected\n" +
+ "----------\n" +
+ "4. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/src/mod.one/module-info.java (at line 3)\n" +
+ " }\n" +
+ " ^\n" +
+ "Syntax error on token \"}\", delete this token\n" +
+ "----------\n" +
+ "4 problems (4 errors)\n",
+ false,
+ OUTPUT_DIR + File.separator + out);
+ }
+ public void testBug522472c() {
+ File outputDirectory = new File(OUTPUT_DIR);
+ Util.flushDirectoryContent(outputDirectory);
+ String out = "bin";
+ String directory = OUTPUT_DIR + File.separator + "src";
+ File srcDir = new File(directory);
+ String moduleLoc = directory + File.separator + "mod.one";
+ List<String> files = new ArrayList<>();
+ writeFileCollecting(files, moduleLoc,
+ "module-info.java",
+ "module mod.one { \n" +
+ " exports x.y.z;\n" +
+ " exports a.b.c;\n" +
+ "}");
+ writeFileCollecting(files, moduleLoc + File.separator + "x" + File.separator + "y" + File.separator + "z",
+ "X.java",
+ "package x.y.z;\n");
+ writeFileCollecting(files, moduleLoc + File.separator + "a" + File.separator + "b" + File.separator + "c",
+ "A.java",
+ "package a.b.c;\n" +
+ "public class A {}");
+
+ moduleLoc = directory + File.separator + "mod.one.a";
+ writeFileCollecting(files, moduleLoc,
+ "module-info.java",
+ "module mod.one.a { \n" +
+ " exports x.y.z;\n" +
+ "}");
+ writeFileCollecting(files, moduleLoc + File.separator + "x" + File.separator + "y" + File.separator + "z",
+ "X.java",
+ "package x.y.z;\n" +
+ "public class X {}\n");
+ StringBuffer buffer = new StringBuffer();
+ buffer.append("-d " + OUTPUT_DIR + File.separator + out )
+ .append(" -9 ")
+ .append(" -classpath \"")
+ .append(Util.getJavaClassLibsAsString())
+ .append("\" ")
+ .append(" --module-source-path " + "\"" + directory + "\" ");
+
+ runConformModuleTest(files,
+ buffer,
+ "",
+ "",
+ false);
+
+ Util.flushDirectoryContent(srcDir);
+ files.clear();
+ moduleLoc = directory + File.separator + "mod.two";
+ writeFileCollecting(files, moduleLoc,
+ "module-info.java",
+ "module mod.two { \n" +
+ " requires mod.one;\n" +
+ " requires mod.one.a;\n" +
+ "}");
+ writeFileCollecting(files, moduleLoc + File.separator + "p" + File.separator + "q" + File.separator + "r",
+ "Main.java",
+ "package p.q.r;\n" +
+ "import a.b.c.*;\n" +
+ "import x.y.z.*;\n" +
+ "@SuppressWarnings(\"unused\")\n" +
+ "public class Main {"
+ + "}");
+ buffer = new StringBuffer();
+ buffer.append("-d " + OUTPUT_DIR + File.separator + out )
+ .append(" -9 ")
+ .append(" -classpath \"")
+ .append(Util.getJavaClassLibsAsString())
+ .append("\" ")
+ .append(" --module-path " + "\"" + OUTPUT_DIR + File.separator + out + "\" ")
+ .append(" --module-source-path " + "\"" + directory + "\" ");
+ runConformModuleTest(files,
+ buffer,
+ "",
+ "",
+ false);
+ }
+ public void testReleaseOption1() throws Exception {
+ this.runConformTest(
+ new String[] {
+ "X.java",
+ "/** */\n" +
+ "public class X {\n" +
+ "}",
+ },
+ "\"" + OUTPUT_DIR + File.separator + "X.java\""
+ + " --release 8 -d \"" + OUTPUT_DIR + "\"",
+ "",
+ "",
+ true);
+ String expectedOutput = "// Compiled from X.java (version 1.8 : 52.0, super bit)";
+ checkDisassembledClassFile(OUTPUT_DIR + File.separator + "X.class", "X", expectedOutput);
+ }
+ public void testReleaseOption2() throws Exception {
+ this.runConformTest(
+ new String[] {
+ "X.java",
+ "/** */\n" +
+ "public class X {\n" +
+ "}",
+ },
+ "\"" + OUTPUT_DIR + File.separator + "X.java\""
+ + " --release 7 -d \"" + OUTPUT_DIR + "\"",
+ "",
+ "",
+ true);
+ String expectedOutput = "// Compiled from X.java (version 1.7 : 51.0, super bit)";
+ checkDisassembledClassFile(OUTPUT_DIR + File.separator + "X.class", "X", expectedOutput);
+ }
+ public void testReleaseOption3() throws Exception {
+ this.runConformTest(
+ new String[] {
+ "X.java",
+ "/** */\n" +
+ "public class X {\n" +
+ "}",
+ },
+ "\"" + OUTPUT_DIR + File.separator + "X.java\""
+ + " --release 6 -d \"" + OUTPUT_DIR + "\"",
+ "",
+ "",
+ true);
+ String expectedOutput = "// Compiled from X.java (version 1.6 : 50.0, super bit)";
+ checkDisassembledClassFile(OUTPUT_DIR + File.separator + "X.class", "X", expectedOutput);
+ }
+ public void testReleaseOption4() throws Exception {
+ this.runNegativeTest(
+ new String[] {
+ "X.java",
+ "/** */\n" +
+ "public class X {\n" +
+ "}",
+ },
+ "\"" + OUTPUT_DIR + File.separator + "X.java\""
+ + " --release 6 -source 1.6 -d \"" + OUTPUT_DIR + "\"",
+ "",
+ "option -source is not supported when --release is used\n",
+ true);
+ }
+ public void testReleaseOption5() throws Exception {
+ this.runNegativeTest(
+ new String[] {
+ "X.java",
+ "/** */\n" +
+ "public class X {\n" +
+ "}",
+ },
+ "\"" + OUTPUT_DIR + File.separator + "X.java\""
+ + " --release 8 -target 1.8 -d \"" + OUTPUT_DIR + "\"",
+ "",
+ "option -target is not supported when --release is used\n",
+ true);
+ }
+ public void testReleaseOption6() throws Exception {
+ this.runNegativeTest(
+ new String[] {
+ "X.java",
+ "/** */\n" +
+ "public class X {\n" +
+ "}",
+ },
+ "\"" + OUTPUT_DIR + File.separator + "X.java\""
+ + " --release 5 -d \"" + OUTPUT_DIR + "\"",
+ "",
+ "release version 5 is not supported\n",
+ true);
+ }
+ public void testReleaseOption7() throws Exception {
+ this.runConformTest(
+ new String[] {
+ "X.java",
+ "import java.util.stream.*;\n" +
+ "/** */\n" +
+ "public class X {\n" +
+ " public Stream<String> emptyStream() {\n" +
+ " Stream<String> st = Stream.empty();\n" +
+ " return st;\n" +
+ " }\n" +
+ "}",
+ },
+ "\"" + OUTPUT_DIR + File.separator + "X.java\""
+ + " --release 8 -d \"" + OUTPUT_DIR + "\"",
+ "",
+ "",
+ true);
+ }
+ public void testReleaseOption8() throws Exception {
+ this.runNegativeTest(
+ new String[] {
+ "X.java",
+ "/** */\n" +
+ "public class X {\n" +
+ " public java.util.stream.Stream<String> emptyStream() {\n" +
+ " return null;\n" +
+ " }\n" +
+ "}",
+ },
+ "\"" + OUTPUT_DIR + File.separator + "X.java\""
+ + " --release 7 -d \"" + OUTPUT_DIR + "\"",
+ "",
+ "----------\n" +
+ "1. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/X.java (at line 3)\n" +
+ " public java.util.stream.Stream<String> emptyStream() {\n" +
+ " ^^^^^^^^^^^^^^^^\n" +
+ "java.util.stream cannot be resolved to a type\n" +
+ "----------\n" +
+ "1 problem (1 error)\n",
+ true);
+ }
+ public void testReleaseOption9() throws Exception {
+ this.runNegativeTest(
+ new String[] {
+ "X.java",
+ "interface I {\n" +
+ " int add(int x, int y);\n" +
+ "}\n" +
+ "public class X {\n" +
+ " public static void main(String[] args) {\n" +
+ " I i = (x, y) -> {\n" +
+ " return x + y;\n" +
+ " };\n" +
+ " }\n" +
+ "}\n",
+ },
+ "\"" + OUTPUT_DIR + File.separator + "X.java\""
+ + " --release 7 -d \"" + OUTPUT_DIR + "\"",
+ "",
+ "----------\n" +
+ "1. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/X.java (at line 6)\n" +
+ " I i = (x, y) -> {\n" +
+ " ^^^^^^^^^\n" +
+ "Lambda expressions are allowed only at source level 1.8 or above\n" +
+ "----------\n" +
+ "1 problem (1 error)\n",
+ true);
+ }
+ public void testReleaseOption10() throws Exception {
+ this.runNegativeTest(
+ new String[] {
+ "X.java",
+ "import java.io.*;\n" +
+ "\n" +
+ "public class X {\n" +
+ " public static void main(String[] args) {\n" +
+ " try {\n" +
+ " System.out.println();\n" +
+ " Reader r = new FileReader(args[0]);\n" +
+ " r.read();\n" +
+ " } catch(IOException | FileNotFoundException e) {\n" +
+ " e.printStackTrace();\n" +
+ " }\n" +
+ " }\n" +
+ "}",
+ },
+ "\"" + OUTPUT_DIR + File.separator + "X.java\""
+ + " --release 6 -d \"" + OUTPUT_DIR + "\"",
+ "",
+ "----------\n" +
+ "1. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/X.java (at line 9)\n" +
+ " } catch(IOException | FileNotFoundException e) {\n" +
+ " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" +
+ "Multi-catch parameters are not allowed for source level below 1.7\n" +
+ "----------\n" +
+ "2. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/X.java (at line 9)\n" +
+ " } catch(IOException | FileNotFoundException e) {\n" +
+ " ^^^^^^^^^^^^^^^^^^^^^\n" +
+ "The exception FileNotFoundException is already caught by the alternative IOException\n" +
+ "----------\n" +
+ "2 problems (2 errors)\n",
+ true);
+ }
+ public void testReleaseOption11() throws Exception {
+ this.runNegativeTest(
+ new String[] {
+ "X.java",
+ "public class X {\n" +
+ " public static void main(String[] args) {\n" +
+ " }\n" +
+ "}",
+ },
+ "\"" + OUTPUT_DIR + File.separator + "X.java\"" +
+ " -bootclasspath " + OUTPUT_DIR + File.separator + "src " +
+ " --release 9 -d \"" + OUTPUT_DIR + "\"",
+ "",
+ "option -bootclasspath not supported at compliance level 9 and above\n",
+ true);
+ }
+ public void _testReleaseOption12() throws Exception {
+ String javaHome = System.getProperty("java.home");
+ this.runNegativeTest(
+ new String[] {
+ "X.java",
+ "public class X {\n" +
+ " public static void main(String[] args) {\n" +
+ " }\n" +
+ "}",
+ },
+ "\"" + OUTPUT_DIR + File.separator + "X.java\"" +
+ " --system \"" + javaHome + "\"" +
+ " --release 6 -d \"" + OUTPUT_DIR + "\"",
+ "",
+ "----------\n" +
+ "option --system not supported below compliance level 9",
+ true);
+ }
+ public void testReleaseOption13() {
+ runConformModuleTest(
+ new String[] {
+ "p/X.java",
+ "package p;\n" +
+ "public class X {\n" +
+ " public static void main(String[] args) {\n" +
+ " }\n" +
+ "}",
+ "module-info.java",
+ "module mod.one { \n" +
+ " requires java.base;\n" +
+ "}"
+ },
+ " --release 9 \"" + OUTPUT_DIR + File.separator + "module-info.java\" "
+ + "\"" + OUTPUT_DIR + File.separator + "p/X.java\"",
+ "",
+ "",
+ true);
+ }
+ public void testReleaseOption14() {
+ runNegativeModuleTest(
+ new String[] {
+ "module-info.java",
+ "module mod.one { \n" +
+ "}"
+ },
+ " --release 8 \"" + OUTPUT_DIR + File.separator + "module-info.java\" ",
+ "",
+ "----------\n" +
+ "1. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/module-info.java (at line 1)\n" +
+ " module mod.one { \n" +
+ " ^^^^^^\n" +
+ "Syntax error on token \"module\", package expected\n" +
+ "----------\n" +
+ "2. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/module-info.java (at line 1)\n" +
+ " module mod.one { \n" +
+ "}\n" +
+ " ^^^^\n" +
+ "Syntax error on tokens, delete these tokens\n" +
+ "----------\n" +
+ "2 problems (2 errors)\n",
+ true,
+ /*not tested with javac*/"");
+ }
+ // Test from https://bugs.eclipse.org/bugs/show_bug.cgi?id=526997
+ public void testReleaseOption15() {
+ runConformModuleTest(
+ new String[] {
+ "foo/Module.java",
+ "package foo;\n" +
+ "public class Module {}\n",
+ "foo/X.java",
+ "package foo;\n" +
+ "public class X { \n" +
+ " public Module getModule(String name) {\n" +
+ " return null;\n" +
+ " }\n" +
+ "}"
+ },
+ " --release 8 \"" + OUTPUT_DIR + File.separator + "foo" + File.separator + "Module.java\" " +
+ "\"" + OUTPUT_DIR + File.separator + "foo" + File.separator + "X.java\" ",
+ "",
+ "",
+ true,
+ /*not tested with javac*/"");
+ }
+ // Test from https://bugs.eclipse.org/bugs/show_bug.cgi?id=526997
+ public void testReleaseOption16() {
+ runNegativeModuleTest(
+ new String[] {
+ "foo/Module.java",
+ "package foo;\n" +
+ "public class Module {}\n",
+ "bar/X.java",
+ "package bar;\n" +
+ "import foo.*;\n" +
+ "public class X { \n" +
+ " public Module getModule(String name) {\n" +
+ " return null;\n" +
+ " }\n" +
+ "}"
+ },
+ " -source 9 \"" + OUTPUT_DIR + File.separator + "foo" + File.separator + "Module.java\" " +
+ "\"" + OUTPUT_DIR + File.separator + "bar" + File.separator + "X.java\" ",
+ "",
+ "----------\n" +
+ "1. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/bar/X.java (at line 4)\n" +
+ " public Module getModule(String name) {\n" +
+ " ^^^^^^\n" +
+ "The type Module is ambiguous\n" +
+ "----------\n" +
+ "1 problem (1 error)\n",
+ true,
+ /*not tested with javac*/"");
+ }
+ public void testReleaseOption17() {
+ runNegativeModuleTest(
+ new String[] {
+ "foo/Module.java",
+ "package foo;\n" +
+ "public class Module {}\n",
+ "foo/X.java",
+ "package foo;\n" +
+ "public class X { \n" +
+ " public Module getModule(String name) {\n" +
+ " return null;\n" +
+ " }\n" +
+ "}"
+ },
+ " --release 60 \"" + OUTPUT_DIR + File.separator + "foo" + File.separator + "Module.java\" " +
+ "\"" + OUTPUT_DIR + File.separator + "foo" + File.separator + "X.java\" ",
+ "",
+ "release 60 is not found in the system\n",
+ true,
+ /*not tested with javac*/"");
+ }
+ public void testReleaseOption18() {
+ runNegativeModuleTest(
+ new String[] {
+ "X.java",
+ "/** */\n" +
+ "public class X {\n" +
+ "}",
+ },
+ " --release 6 -1.8 \"" + OUTPUT_DIR + File.separator + "foo" + File.separator + "Module.java\" " +
+ "\"" + OUTPUT_DIR + File.separator + "foo" + File.separator + "X.java\" ",
+ "",
+ "option 1.8 is not supported when --release is used\n",
+ true,
+ /*not tested with javac*/"");
+ }
+ public void testReleaseOption19() {
+ runNegativeModuleTest(
+ new String[] {
+ "X.java",
+ "/** */\n" +
+ "public class X {\n" +
+ "}",
+ },
+ " -9 --release 9 \"" + OUTPUT_DIR + File.separator + "foo" + File.separator + "Module.java\" " +
+ "\"" + OUTPUT_DIR + File.separator + "foo" + File.separator + "X.java\" ",
+ "",
+ "option 9 is not supported when --release is used\n",
+ true,
+ /*not tested with javac*/"");
+ }
+ public void testLimitModules1() {
+ File outputDirectory = new File(OUTPUT_DIR);
+ Util.flushDirectoryContent(outputDirectory);
+ String out = "bin";
+ String directory = OUTPUT_DIR + File.separator + "src";
+
+ String moduleLoc = directory + File.separator + "mod.x";
+ List<String> files = new ArrayList<>();
+ writeFileCollecting(files, moduleLoc, "module-info.java",
+ "module mod.x { \n" +
+ " exports pm;\n" +
+ " requires java.sql;\n" +
+ "}");
+ writeFileCollecting(files, moduleLoc + File.separator + "pm", "C1.java",
+ "package pm;\n" +
+ "public class C1 {\n" +
+ "}\n");
+
+
+ StringBuffer buffer = new StringBuffer();
+ buffer.append("-d " + OUTPUT_DIR + File.separator + out )
+ .append(" -9 ")
+ .append(" -classpath \"")
+ .append(Util.getJavaClassLibsAsString())
+ .append("\" ")
+ .append(" --limit-modules java.base")
+ .append(" --module-source-path " + "\"" + directory + "\"");
+ runNegativeModuleTest(files, buffer,
+ "",
+ "----------\n" +
+ "1. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/src/mod.x/module-info.java (at line 3)\n" +
+ " requires java.sql;\n" +
+ " ^^^^^^^^\n" +
+ "java.sql cannot be resolved to a module\n" +
+ "----------\n" +
+ "1 problem (1 error)\n",
+ false,
+ "is not accessible to clients");
+ }
+ public void testLimitModules2() {
+ File outputDirectory = new File(OUTPUT_DIR);
+ Util.flushDirectoryContent(outputDirectory);
+ String out = "bin";
+ String directory = OUTPUT_DIR + File.separator + "src";
+
+ String moduleLoc = directory + File.separator + "mod.x";
+ List<String> files = new ArrayList<>();
+ writeFileCollecting(files, moduleLoc, "module-info.java",
+ "module mod.x { \n" +
+ " exports pm;\n" +
+ "}");
+ writeFileCollecting(files, moduleLoc + File.separator + "pm", "C1.java",
+ "package pm;\n" +
+ "import java.sql.Connection;\n" +
+ "public class C1 {\n" +
+ "}\n");
+
+
+ StringBuffer buffer = new StringBuffer();
+ buffer.append("-d " + OUTPUT_DIR + File.separator + out )
+ .append(" -9 ")
+ .append(" -classpath \"")
+ .append(Util.getJavaClassLibsAsString())
+ .append("\" ")
+ .append(" --limit-modules java.base")
+ .append(" --module-source-path " + "\"" + directory + "\"");
+ runNegativeModuleTest(files, buffer,
+ "",
+ "----------\n" +
+ "1. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/src/mod.x/pm/C1.java (at line 2)\n" +
+ " import java.sql.Connection;\n" +
+ " ^^^^^^^^\n" +
+ "The import java.sql cannot be resolved\n" +
+ "----------\n" +
+ "1 problem (1 error)\n",
+ false,
+ "is not accessible to clients");
+ }
+ public void testLimitModules3() {
+ File outputDirectory = new File(OUTPUT_DIR);
+ Util.flushDirectoryContent(outputDirectory);
+ String out = "bin";
+ String directory = OUTPUT_DIR + File.separator + "src";
+
+ String moduleLoc = directory + File.separator + "mod.x";
+ List<String> files = new ArrayList<>();
+ writeFileCollecting(files, moduleLoc, "module-info.java",
+ "module mod.x { \n" +
+ " exports pm;\n" +
+ "}");
+ writeFileCollecting(files, moduleLoc + File.separator + "pm", "C1.java",
+ "package pm;\n" +
+ "public class C1 {\n" +
+ "}\n");
+
+
+ StringBuffer buffer = new StringBuffer();
+ buffer.append("-d " + OUTPUT_DIR + File.separator + out )
+ .append(" -9 ")
+ .append(" -classpath \"")
+ .append(Util.getJavaClassLibsAsString())
+ .append("\" ")
+ .append(" --limit-modules java.sql")
+ .append(" --module-source-path " + "\"" + directory + "\"");
+ runConformModuleTest(files, buffer,
+ "",
+ "",
+ false);
+ }
+ public void testLimitModules4() {
+ Util.flushDirectoryContent(new File(OUTPUT_DIR));
+ String outDir = OUTPUT_DIR + File.separator + "bin";
+ String srcDir = OUTPUT_DIR + File.separator + "src";
+ File modDir = new File(OUTPUT_DIR + File.separator + "mod");
+ createReusableModules(srcDir, outDir, modDir);
+ String moduleLoc = srcDir + File.separator + "mod.three";
+ List<String> files = new ArrayList<>();
+ writeFileCollecting(files, moduleLoc, "module-info.java",
+ "module mod.three { \n" +
+ " requires mod.one;\n" +
+ " requires mod.two;\n" +
+ "}");
+
+
+ StringBuffer buffer = new StringBuffer();
+ buffer.append("-d " + outDir )
+ .append(" -9 ")
+ .append(" --module-path \"")
+ .append(Util.getJavaClassLibsAsString())
+ .append(modDir.getAbsolutePath())
+ .append("\" ")
+ .append(" --limit-modules mod.one,mod.two ")
+ .append(" --module-source-path " + "\"" + srcDir + "\" ");
+ runConformModuleTest(files, buffer,
+ "",
+ "",
+ false);
+ }
+ public void testLimitModules5() {
+ Util.flushDirectoryContent(new File(OUTPUT_DIR));
+ String outDir = OUTPUT_DIR + File.separator + "bin";
+ String srcDir = OUTPUT_DIR + File.separator + "src";
+ File modDir = new File(OUTPUT_DIR + File.separator + "mod");
+ createReusableModules(srcDir, outDir, modDir);
+ String moduleLoc = srcDir + File.separator + "mod.three";
+ List<String> files = new ArrayList<>();
+ writeFileCollecting(files, moduleLoc, "module-info.java",
+ "module mod.three { \n" +
+ " requires mod.one;\n" +
+ " requires mod.two;\n" +
+ "}");
+
+
+ StringBuffer buffer = new StringBuffer();
+ buffer.append("-d " + outDir )
+ .append(" -9 ")
+ .append(" --module-path \"")
+ .append(Util.getJavaClassLibsAsString())
+ .append(modDir.getAbsolutePath())
+ .append("\" ")
+ .append(" --limit-modules mod.one ")
+ .append(" --module-source-path " + "\"" + srcDir + "\" ");
+ runNegativeModuleTest(files, buffer,
+ "",
+ "----------\n" +
+ "1. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/src/mod.three/module-info.java (at line 3)\n" +
+ " requires mod.two;\n" +
+ " ^^^^^^^\n" +
+ "mod.two cannot be resolved to a module\n" +
+ "----------\n" +
+ "1 problem (1 error)\n",
+ false,
+ "");
+ }
}
diff --git a/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/ClasspathJep247.java b/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/ClasspathJep247.java
new file mode 100644
index 0000000..69f9a6a
--- /dev/null
+++ b/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/ClasspathJep247.java
@@ -0,0 +1,215 @@
+package org.eclipse.jdt.internal.compiler.batch;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URI;
+import java.nio.file.DirectoryStream;
+import java.nio.file.FileSystemNotFoundException;
+import java.nio.file.FileSystems;
+import java.nio.file.FileVisitResult;
+import java.nio.file.FileVisitor;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.jdt.core.compiler.CharOperation;
+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.AccessRuleSet;
+import org.eclipse.jdt.internal.compiler.env.IModule;
+import org.eclipse.jdt.internal.compiler.env.NameEnvironmentAnswer;
+import org.eclipse.jdt.internal.compiler.util.Util;
+
+public class ClasspathJep247 extends ClasspathLocation {
+
+ private java.nio.file.FileSystem fs = null;
+ private String release = null;
+ private String[] subReleases = null;
+ private Path releasePath = null;
+ private File file = null;
+ private Set<String> packageCache;
+
+ public ClasspathJep247(File jdkHome, String release, AccessRuleSet accessRuleSet) {
+ super(accessRuleSet, null);
+ this.release = release;
+ this.file = jdkHome;
+ }
+ public List<Classpath> fetchLinkedJars(FileSystem.ClasspathSectionProblemReporter problemReporter) {
+ return null;
+ }
+ public NameEnvironmentAnswer findClass(char[] typeName, String qualifiedPackageName, String moduleName, String qualifiedBinaryFileName) {
+ return findClass(typeName, qualifiedPackageName, moduleName, qualifiedBinaryFileName, false);
+ }
+ public NameEnvironmentAnswer findClass(char[] typeName, String qualifiedPackageName, String moduleName, String qualifiedBinaryFileName, boolean asBinaryOnly) {
+ if (!isPackage(qualifiedPackageName, moduleName))
+ return null; // most common case
+
+ try {
+ //TODO: Check if any conversion needed for path separator
+ ClassFileReader reader = null;
+ byte[] content = null;
+ qualifiedBinaryFileName = qualifiedBinaryFileName.replace(".class", ".sig"); //$NON-NLS-1$ //$NON-NLS-2$
+ if (this.subReleases != null && this.subReleases.length > 0) {
+ for (String rel : this.subReleases) {
+ Path p = this.fs.getPath(rel, qualifiedBinaryFileName);
+ if (Files.exists(p)) {
+ content = Files.readAllBytes(p);
+ if (content != null)
+ break;
+ }
+ }
+ } else {
+ content = Files.readAllBytes(this.fs.getPath(this.release, qualifiedBinaryFileName));
+ }
+ if (content != null) {
+ reader = new ClassFileReader(content, qualifiedBinaryFileName.toCharArray());
+ return new NameEnvironmentAnswer(reader, fetchAccessRestriction(qualifiedBinaryFileName), null);
+ }
+ } catch(ClassFormatException e) {
+ // Continue
+ } catch (IOException e) {
+ // continue
+ }
+ return null;
+ }
+ @Override
+ public boolean hasAnnotationFileFor(String qualifiedTypeName) {
+ return false;
+ }
+ public char[][][] findTypeNames(final String qualifiedPackageName, String moduleName) {
+ // TODO: Revisit
+ return null;
+ }
+
+ public void initialize() throws IOException {
+ if (this.release == null) {
+ return;
+ }
+ Path filePath = this.file.toPath().resolve("lib").resolve("ct.sym"); //$NON-NLS-1$ //$NON-NLS-2$
+ URI t = filePath.toUri();
+ if (!Files.exists(filePath)) {
+ return;
+ }
+ URI uri = URI.create("jar:file:" + t.getPath()); //$NON-NLS-1$
+ try {
+ this.fs = FileSystems.getFileSystem(uri);
+ } catch(FileSystemNotFoundException fne) {
+ // Ignore and move on
+ }
+ if (this.fs == null) {
+ HashMap<String, ?> env = new HashMap<>();
+ this.fs = FileSystems.newFileSystem(uri, env);
+ }
+ this.releasePath = this.fs.getPath(""); //$NON-NLS-1$
+ if (!Files.exists(this.fs.getPath(this.release))) {
+ throw new IllegalArgumentException("release " + this.release + " is not found in the system"); //$NON-NLS-1$//$NON-NLS-2$
+ }
+ }
+ void acceptModule(ClassFileReader reader) {
+ // Nothing to do
+ }
+ protected void addToPackageCache(String packageName, boolean endsWithSep) {
+ if (this.packageCache.contains(packageName))
+ return;
+ this.packageCache.add(packageName);
+ }
+ public synchronized char[][] getModulesDeclaringPackage(String qualifiedPackageName, String moduleName) {
+ // Ignore moduleName as this has nothing to do with modules (as of now)
+ if (this.packageCache != null)
+ return singletonModuleNameIf(this.packageCache.contains(qualifiedPackageName));
+
+ this.packageCache = new HashSet<>(41);
+ this.packageCache.add(Util.EMPTY_STRING);
+ List<String> sub = new ArrayList<>();
+ try (DirectoryStream<java.nio.file.Path> stream = Files.newDirectoryStream(this.releasePath)) {
+ for (final java.nio.file.Path subdir: stream) {
+ String rel = subdir.getFileName().toString();
+ if (rel.contains(this.release)) {
+ sub.add(rel);
+ } else {
+ continue;
+ }
+ Files.walkFileTree(subdir, new FileVisitor<java.nio.file.Path>() {
+ @Override
+ public FileVisitResult preVisitDirectory(java.nio.file.Path dir, BasicFileAttributes attrs) throws IOException {
+ if (dir.getNameCount() <= 1)
+ return FileVisitResult.CONTINUE;
+ Path relative = dir.subpath(1, dir.getNameCount());
+ addToPackageCache(relative.toString(), false);
+ return FileVisitResult.CONTINUE;
+ }
+
+ @Override
+ public FileVisitResult visitFile(java.nio.file.Path f, BasicFileAttributes attrs) throws IOException {
+ return FileVisitResult.CONTINUE;
+ }
+
+ @Override
+ public FileVisitResult visitFileFailed(java.nio.file.Path f, IOException exc) throws IOException {
+ return FileVisitResult.CONTINUE;
+ }
+
+ @Override
+ public FileVisitResult postVisitDirectory(java.nio.file.Path dir, IOException exc) throws IOException {
+ return FileVisitResult.CONTINUE;
+ }
+ });
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ // Rethrow
+ }
+ this.subReleases = sub.toArray(new String[sub.size()]);
+ return singletonModuleNameIf(this.packageCache.contains(qualifiedPackageName));
+ }
+ @Override
+ public boolean hasCompilationUnit(String qualifiedPackageName, String moduleName) {
+ // TOOD: Revisit
+ return false;
+ }
+ public void reset() {
+ try {
+ this.fs.close();
+ } catch (IOException e) {
+ // Move on
+ }
+ }
+ public String toString() {
+ return "Classpath for JEP 247 for JDK " + this.file.getPath(); //$NON-NLS-1$
+ }
+ public char[] normalizedPath() {
+ if (this.normalizedPath == null) {
+ String path2 = this.getPath();
+ char[] rawName = path2.toCharArray();
+ if (File.separatorChar == '\\') {
+ CharOperation.replace(rawName, '\\', '/');
+ }
+ this.normalizedPath = CharOperation.subarray(rawName, 0, CharOperation.lastIndexOf('.', rawName));
+ }
+ return this.normalizedPath;
+ }
+ public String getPath() {
+ if (this.path == null) {
+ try {
+ this.path = this.file.getCanonicalPath();
+ } catch (IOException e) {
+ // in case of error, simply return the absolute path
+ this.path = this.file.getAbsolutePath();
+ }
+ }
+ return this.path;
+ }
+ public int getMode() {
+ return BINARY;
+ }
+
+ public IModule getModule() {
+ return null;
+ }
+}
diff --git a/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/ClasspathJrt.java b/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/ClasspathJrt.java
index be2e520..d8830a1 100644
--- a/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/ClasspathJrt.java
+++ b/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/ClasspathJrt.java
@@ -21,7 +21,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.stream.Collectors;
+import java.util.function.Function;
import java.util.zip.ZipFile;
import org.eclipse.jdt.core.compiler.CharOperation;
@@ -33,6 +33,7 @@
import org.eclipse.jdt.internal.compiler.env.IModule;
import org.eclipse.jdt.internal.compiler.env.IMultiModuleEntry;
import org.eclipse.jdt.internal.compiler.env.NameEnvironmentAnswer;
+import org.eclipse.jdt.internal.compiler.env.IModule.IPackageExport;
import org.eclipse.jdt.internal.compiler.lookup.BinaryTypeBinding.ExternalAnnotationStatus;
import org.eclipse.jdt.internal.compiler.util.JRTUtil;
import org.eclipse.jdt.internal.compiler.util.SuffixConstants;
@@ -250,11 +251,41 @@
}
@Override
- public Collection<String> getModuleNames(Collection<String> limitModule) {
- return ModulesCache.values().stream()
- .flatMap(entryMap -> entryMap.keySet().stream())
- .filter(m -> limitModule == null || limitModule.contains(m)) // TODO: implement algo from JEP 261 (root selection & transitive closure)
- .collect(Collectors.toList());
+ public Collection<String> getModuleNames(Collection<String> limitModule, Function<String, IModule> getModule) {
+ Map<String, IModule> cache = ModulesCache.get(this.file.getPath());
+ return selectModules(cache.keySet(), limitModule, getModule);
+ }
+ protected <T> List<String> allModules(Iterable<T> allSystemModules, Function<T,String> getModuleName, Function<T,IModule> getModule) {
+ List<String> result = new ArrayList<>();
+ boolean hasJavaDotSE = false;
+ for (T mod : allSystemModules) {
+ String moduleName = getModuleName.apply(mod);
+ if ("java.se".equals(moduleName)) { //$NON-NLS-1$
+ result.add(moduleName);
+ hasJavaDotSE = true;
+ break;
+ }
+ }
+ for (T mod : allSystemModules) {
+ String moduleName = getModuleName.apply(mod);
+ boolean isJavaDotStart = moduleName.startsWith("java."); //$NON-NLS-1$
+ boolean isPotentialRoot = !isJavaDotStart; // always include non-java.*
+ if (!hasJavaDotSE)
+ isPotentialRoot |= isJavaDotStart; // no java.se => add all java.*
+
+ if (isPotentialRoot) {
+ IModule m = getModule.apply(mod);
+ if (m != null) {
+ for (IPackageExport packageExport : m.exports()) {
+ if (!packageExport.isQualified()) {
+ result.add(moduleName);
+ break;
+ }
+ }
+ }
+ }
+ }
+ return result;
}
// protected void addToPackageCache(String fileName, boolean endsWithSep) {
// int last = endsWithSep ? fileName.length() : fileName.lastIndexOf('/');
diff --git a/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/ClasspathLocation.java b/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/ClasspathLocation.java
index 4b2d508..2426b61 100644
--- a/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/ClasspathLocation.java
+++ b/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/ClasspathLocation.java
@@ -11,13 +11,19 @@
package org.eclipse.jdt.internal.compiler.batch;
import java.io.File;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Function;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.env.AccessRestriction;
import org.eclipse.jdt.internal.compiler.env.AccessRuleSet;
import org.eclipse.jdt.internal.compiler.env.IModule;
+import org.eclipse.jdt.internal.compiler.env.IModule.IModuleReference;
import org.eclipse.jdt.internal.compiler.lookup.ModuleBinding;
import org.eclipse.jdt.internal.compiler.util.SuffixConstants;
@@ -120,10 +126,52 @@
}
@Override
public Collection<String> getModuleNames(Collection<String> limitModules) {
- if (this.module != null)
- return Collections.singletonList(String.valueOf(this.module.name()));
+ return getModuleNames(limitModules, m -> getModule(m.toCharArray()));
+ }
+ @Override
+ public Collection<String> getModuleNames(Collection<String> limitModules, Function<String,IModule> getModule) {
+ if (this.module != null) {
+ String name = String.valueOf(this.module.name());
+ return selectModules(Collections.singleton(name), limitModules, getModule);
+ }
return Collections.emptyList();
}
+ protected Collection<String> selectModules(Set<String> modules, Collection<String> limitModules, Function<String,IModule> getModule) {
+ Collection<String> rootModules;
+ if (limitModules != null) {
+ Set<String> result = new HashSet<>(modules);
+ result.retainAll(limitModules);
+ rootModules = result;
+ } else {
+ rootModules = allModules(modules, s -> s, m -> getModule(m.toCharArray()));
+ }
+ Set<String> allModules = new HashSet<>(rootModules);
+ for (String mod : rootModules)
+ addRequired(mod, allModules, getModule);
+ return allModules;
+ }
+
+ private void addRequired(String mod, Set<String> allModules, Function<String,IModule> getModule) {
+ IModule iMod = getModule(mod.toCharArray());
+ if (iMod != null) {
+ for (IModuleReference requiredRef : iMod.requires()) {
+ IModule reqMod = getModule.apply(new String(requiredRef.name()));
+ if (reqMod != null) {
+ String reqModName = String.valueOf(reqMod.name());
+ if (allModules.add(reqModName))
+ addRequired(reqModName, allModules, getModule);
+ }
+ }
+ }
+ }
+ protected <T> List<String> allModules(Iterable<T> allSystemModules, Function<T,String> getModuleName, Function<T,IModule> getModule) {
+ List<String> result = new ArrayList<>();
+ for (T mod : allSystemModules) {
+ String moduleName = getModuleName.apply(mod);
+ result.add(moduleName);
+ }
+ return result;
+ }
public boolean isPackage(String qualifiedPackageName, String moduleName) {
return getModulesDeclaringPackage(qualifiedPackageName, moduleName) != null;
diff --git a/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/FileSystem.java b/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/FileSystem.java
index b8fc121..e31eca6 100644
--- a/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/FileSystem.java
+++ b/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/FileSystem.java
@@ -14,6 +14,7 @@
import java.io.File;
import java.io.IOException;
+import java.nio.file.InvalidPathException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
@@ -119,6 +120,7 @@
public void acceptModule(IModule module);
public String getDestinationPath();
Collection<String> getModuleNames(Collection<String> limitModules);
+ Collection<String> getModuleNames(Collection<String> limitModules, Function<String,IModule> getModule);
}
public interface ClasspathSectionProblemReporter {
void invalidClasspathSection(String jarFilePath);
@@ -177,7 +179,7 @@
Classpath classpath = getClasspath(classpathNames[i], encoding, null, null);
try {
classpath.initialize();
- for (String moduleName : classpath.getModuleNames(null)) // TODO limit-modules?
+ for (String moduleName : classpath.getModuleNames(null))
this.moduleLocations.put(moduleName, classpath);
this.classpaths[counter++] = classpath;
} catch (IOException e) {
@@ -189,7 +191,7 @@
}
initializeKnownFileNames(initialFileNames);
}
-protected FileSystem(Classpath[] paths, String[] initialFileNames, boolean annotationsFromClasspath) {
+protected FileSystem(Classpath[] paths, String[] initialFileNames, boolean annotationsFromClasspath, Set<String> limitedModules) {
final int length = paths.length;
int counter = 0;
this.classpaths = new FileSystem.Classpath[length];
@@ -197,10 +199,8 @@
final Classpath classpath = paths[i];
try {
classpath.initialize();
- for (String moduleName : classpath.getModuleNames(null)) // TODO limit-modules?
- this.moduleLocations.put(moduleName, classpath);
this.classpaths[counter++] = classpath;
- } catch(IOException | IllegalArgumentException exception) {
+ } catch(IOException | InvalidPathException exception) {
// JRE 9 could throw an IAE if the linked JAR paths have invalid chars, such as ":"
// ignore
}
@@ -209,9 +209,30 @@
// should not happen
System.arraycopy(this.classpaths, 0, (this.classpaths = new FileSystem.Classpath[counter]), 0, counter);
}
+ initializeModuleLocations(limitedModules);
initializeKnownFileNames(initialFileNames);
this.annotationsFromClasspath = annotationsFromClasspath;
}
+private void initializeModuleLocations(Set<String> limitedModules) {
+ // First create the mapping of all module/Classpath
+ // since the second iteration of getModuleNames() can't be relied on for
+ // to get the right origin of module
+ Map<String, Classpath> moduleMap = new HashMap<>();
+ for (Classpath c : this.classpaths) {
+ for (String moduleName : c.getModuleNames(null)) {
+ moduleMap.put(moduleName, c);
+ }
+ }
+ for (Classpath c : this.classpaths) {
+ for (String moduleName : c.getModuleNames(limitedModules, m -> getModuleFromEnvironment(m.toCharArray()))) {
+ Classpath classpath = moduleMap.get(moduleName);
+ this.moduleLocations.put(moduleName, classpath);
+ }
+ }
+}
+protected FileSystem(Classpath[] paths, String[] initialFileNames, boolean annotationsFromClasspath) {
+ this(paths, initialFileNames, annotationsFromClasspath, null);
+}
public static Classpath getClasspath(String classpathName, String encoding, AccessRuleSet accessRuleSet) {
return getClasspath(classpathName, encoding, false, accessRuleSet, null, null);
}
@@ -221,6 +242,9 @@
public static Classpath getJrtClasspath(String jdkHome, String encoding, AccessRuleSet accessRuleSet, Map<String, String> options) {
return new ClasspathJrt(new File(convertPathSeparators(jdkHome)), true, accessRuleSet, null);
}
+public static Classpath getOlderSystemRelease(String jdkHome, String release, AccessRuleSet accessRuleSet) {
+ return new ClasspathJep247(new File(convertPathSeparators(jdkHome)), release, accessRuleSet);
+}
public static Classpath getClasspath(String classpathName, String encoding,
boolean isSourceOnly, AccessRuleSet accessRuleSet,
String destinationPath, Map<String, String> options) {
@@ -587,6 +611,20 @@
if (this.module != null && CharOperation.equals(name, this.module.name())) {
return this.module;
}
+ if (this.moduleLocations.containsKey(new String(name))) {
+ for (Classpath classpath : this.classpaths) {
+ IModule mod = classpath.getModule(name);
+ if (mod != null) {
+ return mod;
+ }
+ }
+ }
+ return null;
+}
+public IModule getModuleFromEnvironment(char[] name) {
+ if (this.module != null && CharOperation.equals(name, this.module.name())) {
+ return this.module;
+ }
for (Classpath classpath : this.classpaths) {
IModule mod = classpath.getModule(name);
if (mod != null) {
diff --git a/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/Main.java b/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/Main.java
index a3155cf..c363bc7 100644
--- a/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/Main.java
+++ b/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/Main.java
@@ -1364,6 +1364,7 @@
private List<String> addonExports = Collections.EMPTY_LIST;
private List<String> addonReads = Collections.EMPTY_LIST;
public Set<String> rootModules = Collections.EMPTY_SET;
+ public Set<String> limitedModules;
public Locale compilerLocale;
public CompilerOptions compilerOptions; // read-only
@@ -1378,6 +1379,7 @@
// == Main.NONE: absorbent element, do not output class files;
// else: use as the path of the directory into which class files must
// be written.
+ protected String releaseVersion;
private boolean didSpecifySource;
private boolean didSpecifyTarget;
public String[] encodings;
@@ -1403,7 +1405,7 @@
public Logger logger;
public int maxProblems;
public Map<String, String> options;
- long complianceLevel;
+ protected long complianceLevel;
public char[][] ignoreOptionalProblemsFromFolders;
protected PrintWriter out;
public boolean proceed = true;
@@ -1842,6 +1844,8 @@
final int INSIDE_SYSTEM = 27;
final int INSIDE_PROCESSOR_MODULE_PATH_start = 28;
final int INSIDE_ADD_MODULES = 29;
+ final int INSIDE_RELEASE = 30;
+ final int INSIDE_LIMIT_MODULES = 31;
final int DEFAULT = 0;
ArrayList<String> bootclasspaths = new ArrayList<>(DEFAULT_SIZE_CLASSPATH);
@@ -2077,6 +2081,10 @@
mode = INSIDE_MAX_PROBLEMS;
continue;
}
+ if (currentArg.equals("--release")) { //$NON-NLS-1$
+ mode = INSIDE_RELEASE;
+ continue;
+ }
if (currentArg.equals("-source")) { //$NON-NLS-1$
mode = INSIDE_SOURCE;
continue;
@@ -2215,6 +2223,10 @@
mode = INSIDE_ADD_MODULES;
continue;
}
+ if (currentArg.equals("--limit-modules")) { //$NON-NLS-1$
+ mode = INSIDE_LIMIT_MODULES;
+ continue;
+ }
if (currentArg.equals("-sourcepath")) {//$NON-NLS-1$
if (sourcepathClasspathArg != null) {
StringBuffer errorMessage = new StringBuffer();
@@ -2666,6 +2678,10 @@
throw new IllegalArgumentException(
this.bind("configure.duplicateTarget", currentArg));//$NON-NLS-1$
}
+ if (this.releaseVersion != null) {
+ throw new IllegalArgumentException(
+ this.bind("configure.unsupportedWithRelease", "-target"));//$NON-NLS-1$ //$NON-NLS-2$
+ }
this.didSpecifyTarget = true;
if (currentArg.equals("1.1")) { //$NON-NLS-1$
this.options.put(CompilerOptions.OPTION_TargetPlatform, CompilerOptions.VERSION_1_1);
@@ -2723,11 +2739,38 @@
}
mode = DEFAULT;
continue;
+ case INSIDE_RELEASE:
+ // If release is < 9, the following are diasllowed:
+ // bootclasspath, -Xbootclasspath, -Xbootclasspath/a:, -Xbootclasspath/p:,
+ // -endorseddirs, -Djava.endorsed.dirs, -extdirs, -Djava.ext.dirs
+
+ // If release >= 9, the following are disallowed
+ // --system and --upgrade-module-path
+
+ // -source and -target are diasllowed for any --release
+ this.releaseVersion = currentArg;
+ long releaseToJDKLevel = CompilerOptions.releaseToJDKLevel(currentArg);
+ if (releaseToJDKLevel == 0) {
+ throw new IllegalArgumentException(
+ this.bind("configure.unsupportedReleaseVersion", currentArg)); //$NON-NLS-1$
+ }
+ // Let's treat it as regular compliance mode
+ this.complianceLevel = releaseToJDKLevel;
+ String versionAsString = CompilerOptions.versionFromJdkLevel(releaseToJDKLevel);
+ this.options.put(CompilerOptions.OPTION_Compliance, versionAsString);
+ this.options.put(CompilerOptions.OPTION_Source, versionAsString);
+ this.options.put(CompilerOptions.OPTION_TargetPlatform, versionAsString);
+ mode = DEFAULT;
+ continue;
case INSIDE_SOURCE :
if (this.didSpecifySource) {
throw new IllegalArgumentException(
this.bind("configure.duplicateSource", currentArg));//$NON-NLS-1$
}
+ if (this.releaseVersion != null) {
+ throw new IllegalArgumentException(
+ this.bind("configure.unsupportedWithRelease", "-source"));//$NON-NLS-1$ //$NON-NLS-2$
+ }
this.didSpecifySource = true;
if (currentArg.equals("1.3")) { //$NON-NLS-1$
this.options.put(CompilerOptions.OPTION_Source, CompilerOptions.VERSION_1_3);
@@ -2822,6 +2865,16 @@
this.rootModules.add(tokenizer.nextToken().trim());
}
continue;
+ case INSIDE_LIMIT_MODULES:
+ mode = DEFAULT;
+ tokenizer = new StringTokenizer(currentArg, ","); //$NON-NLS-1$
+ while (tokenizer.hasMoreTokens()) {
+ if (this.limitedModules == null) {
+ this.limitedModules = new HashSet<>();
+ }
+ this.limitedModules.add(tokenizer.nextToken().trim());
+ }
+ continue;
case INSIDE_CLASSPATH_start:
mode = DEFAULT;
index += processPaths(newCommandLineArgs, index, currentArg, classpaths);
@@ -3392,7 +3445,8 @@
public FileSystem getLibraryAccess() {
FileSystem nameEnvironment = new FileSystem(this.checkedClasspaths, this.filenames,
- this.annotationsFromClasspath && CompilerOptions.ENABLED.equals(this.options.get(CompilerOptions.OPTION_AnnotationBasedNullAnalysis)));
+ this.annotationsFromClasspath && CompilerOptions.ENABLED.equals(this.options.get(CompilerOptions.OPTION_AnnotationBasedNullAnalysis)),
+ this.limitedModules);
nameEnvironment.module = this.module;
processAddonModuleOptions(nameEnvironment);
return nameEnvironment;
@@ -4669,6 +4723,14 @@
map.put(qName, m);
}
}
+ if (this.limitedModules != null) {
+ for (String m : this.limitedModules) {
+ ModuleBinding mod = environment.getModule(m.toCharArray());
+ if (mod == null) {
+ throw new IllegalArgumentException(this.bind("configure.invalidModuleName", m)); //$NON-NLS-1$
+ }
+ }
+ }
}
private ReferenceBinding[] processClassNames(LookupEnvironment environment) {
// check for .class file presence in case of apt processing
@@ -5088,20 +5150,18 @@
String version = this.options.get(CompilerOptions.OPTION_Compliance);
this.complianceLevel = CompilerOptions.versionToJdkLevel(version);
}
-
- if (this.complianceLevel > ClassFileConstants.JDK1_8) {
- if (bootclasspaths != null && bootclasspaths.size() > 0)
- throw new IllegalArgumentException(
- this.bind("configure.unsupportedOption", "-bootclasspath")); //$NON-NLS-1$ //$NON-NLS-2$
- if (extdirsClasspaths != null && extdirsClasspaths.size() > 0)
- throw new IllegalArgumentException(
- this.bind("configure.unsupportedOption", "-extdirs")); //$NON-NLS-1$ //$NON-NLS-2$
- if (endorsedDirClasspaths != null && endorsedDirClasspaths.size() > 0)
- throw new IllegalArgumentException(
- this.bind("configure.unsupportedOption", "-endorseddirs")); //$NON-NLS-1$ //$NON-NLS-2$
- }
// process bootclasspath, classpath and sourcepaths
- ArrayList<Classpath> allPaths = handleBootclasspath(bootclasspaths, customEncoding);
+ ArrayList<Classpath> allPaths = null;
+ long jdkLevel = validateClasspathOptions(bootclasspaths, endorsedDirClasspaths, extdirsClasspaths);
+
+ if (this.releaseVersion != null && this.complianceLevel < jdkLevel) {
+ // TODO: Revisit for access rules
+ allPaths = new ArrayList<Classpath>();
+ allPaths.add(
+ FileSystem.getOlderSystemRelease(this.javaHomeCache.getAbsolutePath(), this.releaseVersion, null));
+ } else {
+ allPaths = handleBootclasspath(bootclasspaths, customEncoding);
+ }
List<FileSystem.Classpath> cp = handleClasspath(classpaths, customEncoding);
@@ -5165,9 +5225,32 @@
}
return false;
}
+protected long validateClasspathOptions(ArrayList<String> bootclasspaths, ArrayList<String> endorsedDirClasspaths, ArrayList<String> extdirsClasspaths) {
+ if (this.complianceLevel > ClassFileConstants.JDK1_8) {
+ if (bootclasspaths != null && bootclasspaths.size() > 0)
+ throw new IllegalArgumentException(
+ this.bind("configure.unsupportedOption", "-bootclasspath")); //$NON-NLS-1$ //$NON-NLS-2$
+ if (extdirsClasspaths != null && extdirsClasspaths.size() > 0)
+ throw new IllegalArgumentException(
+ this.bind("configure.unsupportedOption", "-extdirs")); //$NON-NLS-1$ //$NON-NLS-2$
+ if (endorsedDirClasspaths != null && endorsedDirClasspaths.size() > 0)
+ throw new IllegalArgumentException(
+ this.bind("configure.unsupportedOption", "-endorseddirs")); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ long jdkLevel = Util.getJDKLevel(getJavaHome());
+ if (jdkLevel < ClassFileConstants.JDK9 && this.releaseVersion != null) {
+ throw new IllegalArgumentException(
+ this.bind("configure.unsupportedReleaseOption")); //$NON-NLS-1$
+ }
+ return jdkLevel;
+}
protected void validateOptions(boolean didSpecifyCompliance) {
if (didSpecifyCompliance) {
- Object version = this.options.get(CompilerOptions.OPTION_Compliance);
+ String version = this.options.get(CompilerOptions.OPTION_Compliance);
+ if (this.releaseVersion != null) {
+ throw new IllegalArgumentException(
+ this.bind("configure.unsupportedWithRelease", version));//$NON-NLS-1$
+ }
if (CompilerOptions.VERSION_1_3.equals(version)) {
if (!this.didSpecifySource) this.options.put(CompilerOptions.OPTION_Source, CompilerOptions.VERSION_1_3);
if (!this.didSpecifyTarget) this.options.put(CompilerOptions.OPTION_TargetPlatform, CompilerOptions.VERSION_1_1);
diff --git a/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/messages.properties b/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/messages.properties
index e079b45..42e6e62 100644
--- a/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/messages.properties
+++ b/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/messages.properties
@@ -60,6 +60,9 @@
configure.duplicateCompliance = duplicate compliance setting specification: {0}
configure.duplicateSource = duplicate source compliance setting specification: {0}
configure.duplicateTarget = duplicate target compliance setting specification: {0}
+configure.unsupportedReleaseOption = option --release is supported only when run with JDK 9 or above
+configure.unsupportedWithRelease = option {0} is not supported when --release is used
+configure.unsupportedReleaseVersion = release version {0} is not supported
configure.source = source level should be comprised in between ''1.3'' and ''1.9'' (or ''5'', ''5.0'', ..., ''9'' or ''9.0''): {0}
configure.invalidSystem = invalid location for system libraries: {0}
configure.unsupportedOption = option {0} not supported at compliance level 9 and above
@@ -209,7 +212,20 @@
\ specify where to find source files for multiple modules\n\
\ -p --module-path <directories separated by {0}>\n\
\ specify where to find application modules\n\
-\ --system <jdk> Override location of system modules\
+\ --system <jdk> Override location of system modules\n\
+\ --add-exports <module>/<package>=<other-module>(,<other-module>)*\n\
+\ specify additional package exports clauses to the\n\
+\ given modules\n\
+\ --add-reads <module>=<other-module>(,<other-module>)*\n\
+\ specify additional modules to be considered as required\n\
+\ by given modules\n\
+\ --add-modules <module>(,<module>)*\n\
+\ specify the additional module names that should be\n\
+\ resolved to be root modules\n\
+\ --limit-modules <module>(,<module>)*\n\
+\ specify the observable module names\n\
+\ --release <release>\n\
+\ compile for a specific VM version\n\
\ \n\
\ Compliance options:\n\
\ -1.3 use 1.3 compliance (-source 1.3 -target 1.1)\n\
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/impl/CompilerOptions.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/impl/CompilerOptions.java
index 0b3ee19..4b74849 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/impl/CompilerOptions.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/impl/CompilerOptions.java
@@ -785,6 +785,23 @@
return Util.EMPTY_STRING; // unknown version
}
+ public static long releaseToJDKLevel(String release) {
+ if (release != null && release.length() > 0) {
+ switch(release.charAt(0)) {
+ case '6':
+ return ClassFileConstants.JDK1_6;
+ case '7':
+ return ClassFileConstants.JDK1_7;
+ case '8':
+ return ClassFileConstants.JDK1_8;
+ case '9':
+ return ClassFileConstants.JDK9;
+ default:
+ return 0; // unknown
+ }
+ }
+ return 0;
+ }
public static long versionToJdkLevel(String versionID) {
String version = versionID;
// verification is optimized for all versions with same length and same "1." prefix