| /* |
| * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| /** |
| * @test |
| * @bug 8142968 8154956 8170987 8171412 |
| * @summary Test --add-modules and --limit-modules; also test the "enabled" modules. |
| * @library /tools/lib |
| * @modules |
| * jdk.compiler/com.sun.tools.javac.api |
| * jdk.compiler/com.sun.tools.javac.code |
| * jdk.compiler/com.sun.tools.javac.main |
| * jdk.compiler/com.sun.tools.javac.model |
| * jdk.compiler/com.sun.tools.javac.processing |
| * jdk.compiler/com.sun.tools.javac.util |
| * jdk.jdeps/com.sun.tools.javap |
| * @build toolbox.ToolBox toolbox.JarTask toolbox.JavacTask toolbox.JavaTask ModuleTestBase |
| * @run main AddLimitMods |
| */ |
| |
| import java.io.File; |
| import java.nio.file.Files; |
| import java.nio.file.Path; |
| import java.util.AbstractMap.SimpleEntry; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.HashSet; |
| import java.util.LinkedHashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Map.Entry; |
| import java.util.Objects; |
| import java.util.Set; |
| |
| import javax.annotation.processing.AbstractProcessor; |
| import javax.annotation.processing.RoundEnvironment; |
| import javax.annotation.processing.SupportedAnnotationTypes; |
| import javax.annotation.processing.SupportedOptions; |
| import javax.lang.model.SourceVersion; |
| import javax.lang.model.element.ModuleElement; |
| import javax.lang.model.element.TypeElement; |
| |
| import com.sun.tools.javac.code.Symbol.ClassSymbol; |
| import com.sun.tools.javac.code.Symtab; |
| import com.sun.tools.javac.model.JavacElements; |
| import com.sun.tools.javac.processing.JavacProcessingEnvironment; |
| import com.sun.tools.javac.util.Context; |
| |
| import toolbox.JarTask; |
| import toolbox.JavacTask; |
| import toolbox.JavaTask; |
| import toolbox.Task; |
| |
| public class AddLimitMods extends ModuleTestBase { |
| |
| public static void main(String... args) throws Exception { |
| AddLimitMods t = new AddLimitMods(); |
| t.runTests(); |
| } |
| |
| @Test |
| public void testManual(Path base) throws Exception { |
| Path moduleSrc = base.resolve("module-src"); |
| Path m1 = moduleSrc.resolve("m1x"); |
| |
| tb.writeJavaFiles(m1, |
| "module m1x { requires m2x; requires m3x; }"); |
| |
| Path m2 = moduleSrc.resolve("m2x"); |
| |
| tb.writeJavaFiles(m2, |
| "module m2x { requires m3x; exports m2x; }", |
| "package m2x; public class M2 {}"); |
| |
| Path m3 = moduleSrc.resolve("m3x"); |
| |
| tb.writeJavaFiles(m3, |
| "module m3x { exports m3x; }", |
| "package m3x; public class M3 {}"); |
| |
| Path modulePath = base.resolve("module-path"); |
| |
| Files.createDirectories(modulePath); |
| |
| new JavacTask(tb) |
| .options("--module-source-path", moduleSrc.toString()) |
| .outdir(modulePath) |
| .files(findJavaFiles(m3)) |
| .run() |
| .writeAll(); |
| |
| new JavacTask(tb) |
| .options("--module-source-path", moduleSrc.toString()) |
| .outdir(modulePath) |
| .files(findJavaFiles(m2)) |
| .run() |
| .writeAll(); |
| |
| //real test |
| new JavacTask(tb) |
| .options("--module-path", modulePath.toString(), |
| "--should-stop:ifNoError=FLOW", |
| "--limit-modules", "java.base") |
| .outdir(modulePath) |
| .files(findJavaFiles(m1)) |
| .run(Task.Expect.FAIL) |
| .writeAll(); |
| |
| new JavacTask(tb) |
| .options("--module-path", modulePath.toString(), |
| "--should-stop:ifNoError=FLOW", |
| "--limit-modules", "java.base", |
| "--add-modules", "m2x") |
| .outdir(modulePath) |
| .files(findJavaFiles(m1)) |
| .run(Task.Expect.FAIL) |
| .writeAll(); |
| |
| new JavacTask(tb) |
| .options("--module-path", modulePath.toString(), |
| "--should-stop:ifNoError=FLOW", |
| "--limit-modules", "java.base", |
| "--add-modules", "m2x,m3x") |
| .outdir(modulePath) |
| .files(findJavaFiles(m1)) |
| .run() |
| .writeAll(); |
| |
| new JavacTask(tb) |
| .options("--module-path", modulePath.toString(), |
| "--should-stop:ifNoError=FLOW", |
| "--limit-modules", "m2x") |
| .outdir(modulePath) |
| .files(findJavaFiles(m1)) |
| .run() |
| .writeAll(); |
| |
| new JavacTask(tb) |
| .options("--module-path", modulePath.toString(), |
| "--should-stop:ifNoError=FLOW", |
| "--limit-modules", "m3x") |
| .outdir(modulePath) |
| .files(findJavaFiles(m1)) |
| .run(Task.Expect.FAIL) |
| .writeAll(); |
| |
| new JavacTask(tb) |
| .options("--module-path", modulePath.toString(), |
| "--should-stop:ifNoError=FLOW", |
| "--limit-modules", "m3x", |
| "--add-modules", "m2x") |
| .outdir(modulePath) |
| .files(findJavaFiles(m1)) |
| .run() |
| .writeAll(); |
| } |
| |
| @Test |
| public void testObservableForUnnamed(Path base) throws Exception { |
| Path src = base.resolve("src"); |
| |
| tb.writeJavaFiles(src, |
| "package test;\n" + |
| "@javax.annotation.Generated(\"test\")\n" + |
| "public class Test {\n" + |
| " com.sun.tools.javac.Main m;\n" + |
| " javax.xml.bind.JAXBException e;\n" + |
| "}\n"); |
| |
| Path out = base.resolve("out"); |
| |
| Files.createDirectories(out); |
| |
| for (Entry<String[], String> variant : variants) { |
| System.err.println("running variant: options=" + Arrays.asList(variant.getKey()) + ", expected log: " + variant.getValue()); |
| |
| List<String> options = new ArrayList<>(); |
| options.add("-XDrawDiagnostics"); |
| options.addAll(Arrays.asList(variant.getKey())); |
| |
| String log = new JavacTask(tb) |
| .options(options.toArray(new String[0])) |
| .outdir(out) |
| .files(findJavaFiles(src)) |
| .run(variant.getValue() == null ? Task.Expect.SUCCESS : Task.Expect.FAIL) |
| .writeAll() |
| .getOutput(Task.OutputKind.DIRECT); |
| |
| log = log.replace(System.getProperty("line.separator"), "\n"); |
| |
| if (variant.getValue() != null && !log.equals(variant.getValue())) { |
| throw new AssertionError(); |
| } |
| } |
| } |
| |
| private static final List<Entry<String[], String>> variants = Arrays.asList( |
| new SimpleEntry<String[], String>(new String[] {}, |
| "Test.java:2:7: compiler.err.package.not.visible: javax.annotation, (compiler.misc.not.def.access.does.not.read.from.unnamed: javax.annotation, java.xml.ws.annotation)\n" |
| + "Test.java:5:14: compiler.err.package.not.visible: javax.xml.bind, (compiler.misc.not.def.access.does.not.read.from.unnamed: javax.xml.bind, java.xml.bind)\n" |
| + "2 errors\n"), |
| new SimpleEntry<String[], String>(new String[] {"--add-modules", "java.xml.ws.annotation,java.xml.bind"}, |
| null), |
| new SimpleEntry<String[], String>(new String[] {"--limit-modules", "java.xml.ws,jdk.compiler"}, |
| null), |
| new SimpleEntry<String[], String>(new String[] {"--add-modules", "ALL-SYSTEM"}, |
| null) |
| ); |
| |
| @Test |
| public void testAllModulePath(Path base) throws Exception { |
| if (Files.isDirectory(base)) |
| tb.cleanDirectory(base); |
| |
| Path moduleSrc = base.resolve("module-src"); |
| Path m1 = moduleSrc.resolve("m1x"); |
| |
| tb.writeJavaFiles(m1, |
| "module m1x { exports api; }", |
| "package api; public class Api { }"); |
| |
| Path modulePath = base.resolve("module-path"); |
| |
| Files.createDirectories(modulePath); |
| |
| new JavacTask(tb) |
| .options("--module-source-path", moduleSrc.toString()) |
| .outdir(modulePath) |
| .files(findJavaFiles(moduleSrc)) |
| .run() |
| .writeAll(); |
| |
| Path cpSrc = base.resolve("cp-src"); |
| tb.writeJavaFiles(cpSrc, "package test; public class Test { api.Api api; }"); |
| |
| Path cpOut = base.resolve("cp-out"); |
| |
| Files.createDirectories(cpOut); |
| |
| new JavacTask(tb) |
| .options("--module-path", modulePath.toString()) |
| .outdir(cpOut) |
| .files(findJavaFiles(cpSrc)) |
| .run(Task.Expect.FAIL) |
| .writeAll(); |
| |
| new JavacTask(tb) |
| .options("--module-path", modulePath.toString(), |
| "--add-modules", "ALL-MODULE-PATH") |
| .outdir(cpOut) |
| .files(findJavaFiles(cpSrc)) |
| .run() |
| .writeAll(); |
| |
| List<String> actual; |
| List<String> expected = Arrays.asList( |
| "- compiler.err.addmods.all.module.path.invalid", |
| "1 error"); |
| |
| actual = new JavacTask(tb) |
| .options("--module-source-path", moduleSrc.toString(), |
| "-XDrawDiagnostics", |
| "--add-modules", "ALL-MODULE-PATH") |
| .outdir(modulePath) |
| .files(findJavaFiles(moduleSrc)) |
| .run(Task.Expect.FAIL) |
| .writeAll() |
| .getOutputLines(Task.OutputKind.DIRECT); |
| |
| if (!Objects.equals(actual, expected)) { |
| throw new IllegalStateException("incorrect errors; actual=" + actual + "; expected=" + expected); |
| } |
| |
| actual = new JavacTask(tb) |
| .options("--patch-module", "java.base=" + cpSrc.toString(), |
| "-XDrawDiagnostics", |
| "--add-modules", "ALL-MODULE-PATH") |
| .outdir(cpOut) |
| .files(findJavaFiles(cpSrc)) |
| .run(Task.Expect.FAIL) |
| .writeAll() |
| .getOutputLines(Task.OutputKind.DIRECT); |
| |
| if (!Objects.equals(actual, expected)) { |
| throw new IllegalStateException("incorrect errors; actual=" + actual + "; expected=" + expected); |
| } |
| |
| actual = new JavacTask(tb, Task.Mode.CMDLINE) |
| .options("-source", "8", "-target", "8", |
| "-XDrawDiagnostics", |
| "--add-modules", "ALL-MODULE-PATH") |
| .outdir(cpOut) |
| .files(findJavaFiles(cpSrc)) |
| .run(Task.Expect.FAIL) |
| .writeAll() |
| .getOutputLines(Task.OutputKind.DIRECT); |
| |
| if (!actual.contains("javac: option --add-modules not allowed with target 1.8")) { |
| throw new IllegalStateException("incorrect errors; actual=" + actual); |
| } |
| |
| tb.writeJavaFiles(cpSrc, "module m1x {}"); |
| |
| actual = new JavacTask(tb) |
| .options("-XDrawDiagnostics", |
| "--add-modules", "ALL-MODULE-PATH") |
| .outdir(cpOut) |
| .files(findJavaFiles(cpSrc)) |
| .run(Task.Expect.FAIL) |
| .writeAll() |
| .getOutputLines(Task.OutputKind.DIRECT); |
| |
| if (!Objects.equals(actual, expected)) { |
| throw new IllegalStateException("incorrect errors; actual=" + actual + "; expected=" + expected); |
| } |
| } |
| |
| @Test |
| public void testRuntime2Compile(Path base) throws Exception { |
| Path classpathSrc = base.resolve("classpath-src"); |
| Path classpathOut = base.resolve("classpath-out"); |
| |
| tb.writeJavaFiles(classpathSrc, |
| generateCheckAccessibleClass("cp.CP")); |
| |
| Files.createDirectories(classpathOut); |
| |
| System.err.println("Compiling classpath-src files:"); |
| new JavacTask(tb) |
| .outdir(classpathOut) |
| .files(findJavaFiles(classpathSrc)) |
| .run() |
| .writeAll() |
| .getOutput(Task.OutputKind.DIRECT); |
| |
| Path automaticSrc = base.resolve("automatic-src"); |
| Path automaticOut = base.resolve("automatic-out"); |
| |
| tb.writeJavaFiles(automaticSrc, |
| generateCheckAccessibleClass("automatic.Automatic")); |
| |
| Files.createDirectories(automaticOut); |
| |
| System.err.println("Compiling automatic-src files:"); |
| new JavacTask(tb) |
| .outdir(automaticOut) |
| .files(findJavaFiles(automaticSrc)) |
| .run() |
| .writeAll() |
| .getOutput(Task.OutputKind.DIRECT); |
| |
| Path modulePath = base.resolve("module-path"); |
| |
| Files.createDirectories(modulePath); |
| |
| Path automaticJar = modulePath.resolve("automatic.jar"); |
| |
| System.err.println("Creating automatic.jar:"); |
| new JarTask(tb, automaticJar) |
| .baseDir(automaticOut) |
| .files("automatic/Automatic.class") |
| .run(); |
| |
| Path moduleSrc = base.resolve("module-src"); |
| Path m1 = moduleSrc.resolve("m1x"); |
| |
| tb.writeJavaFiles(m1, |
| "module m1x { exports api; }", |
| "package api; public class Api { public void test() { } }"); |
| |
| System.err.println("Compiling module-src files:"); |
| new JavacTask(tb) |
| .options("--module-source-path", moduleSrc.toString()) |
| .outdir(modulePath) |
| .files(findJavaFiles(moduleSrc)) |
| .run() |
| .writeAll() |
| .getOutput(Task.OutputKind.DIRECT); |
| |
| int index = 0; |
| |
| for (String moduleInfo : MODULE_INFO_VARIANTS) { |
| for (String[] options : OPTIONS_VARIANTS) { |
| index++; |
| |
| System.err.println("Running check: " + moduleInfo + "; " + Arrays.asList(options)); |
| |
| Path m2Runtime = base.resolve(index + "-runtime").resolve("m2x"); |
| Path out = base.resolve(index + "-runtime").resolve("out").resolve("m2x"); |
| |
| Files.createDirectories(out); |
| |
| StringBuilder testClassNamed = new StringBuilder(); |
| |
| testClassNamed.append("package test;\n" + |
| "public class Test {\n" + |
| " public static void main(String... args) throws Exception {\n"); |
| |
| for (Entry<String, String> e : MODULES_TO_CHECK_TO_SAMPLE_CLASS.entrySet()) { |
| testClassNamed.append(" System.err.println(\"visible:" + e.getKey() + ":\" + ModuleLayer.boot().findModule(\"" + e.getKey() + "\").isPresent());\n"); |
| } |
| |
| testClassNamed.append(" Class<?> cp = Class.forName(Test.class.getClassLoader().getUnnamedModule(), \"cp.CP\");\n"); |
| testClassNamed.append(" cp.getDeclaredMethod(\"runMe\").invoke(null);\n"); |
| |
| testClassNamed.append(" Class<?> automatic = Class.forName(ModuleLayer.boot().findModule(\"automatic\").get(), \"automatic.Automatic\");\n"); |
| testClassNamed.append(" automatic.getDeclaredMethod(\"runMe\").invoke(null);\n"); |
| |
| testClassNamed.append(" }\n" + |
| "}"); |
| |
| tb.writeJavaFiles(m2Runtime, moduleInfo, testClassNamed.toString()); |
| |
| System.err.println("Compiling " + m2Runtime + " files:"); |
| new JavacTask(tb) |
| .options("--module-path", modulePath.toString()) |
| .outdir(out) |
| .files(findJavaFiles(m2Runtime)) |
| .run() |
| .writeAll(); |
| |
| boolean success; |
| String output; |
| |
| try { |
| System.err.println("Running m2x/test.Test:"); |
| output = new JavaTask(tb) |
| .vmOptions(augmentOptions(options, |
| Collections.emptyList(), |
| "--module-path", modulePath.toString() + File.pathSeparator + out.getParent().toString(), |
| "--class-path", classpathOut.toString(), |
| "--add-reads", "m2x=ALL-UNNAMED,automatic", |
| "-m", "m2x/test.Test")) |
| .run() |
| .writeAll() |
| .getOutput(Task.OutputKind.STDERR); |
| |
| success = true; |
| } catch (Task.TaskError err) { |
| success = false; |
| output = ""; |
| } |
| |
| Path m2 = base.resolve(String.valueOf(index)).resolve("m2x"); |
| |
| tb.writeJavaFiles(m2, |
| moduleInfo, |
| "package test;\n" + |
| "public class Test {}\n"); |
| |
| List<String> auxOptions = success ? Arrays.asList( |
| "--processor-path", System.getProperty("test.class.path"), |
| "-processor", CheckVisibleModule.class.getName(), |
| "-Aoutput=" + output, |
| "-XDaccessInternalAPI=true" |
| ) : Collections.emptyList(); |
| |
| System.err.println("Compiling/processing m2x files:"); |
| new JavacTask(tb) |
| .options(augmentOptions(options, |
| auxOptions, |
| "--module-path", modulePath.toString(), |
| "--class-path", classpathOut.toString(), |
| "--should-stop:ifNoError=FLOW")) |
| .outdir(modulePath) |
| .files(findJavaFiles(m2)) |
| .run(success ? Task.Expect.SUCCESS : Task.Expect.FAIL) |
| .writeAll(); |
| } |
| } |
| } |
| |
| private String generateCheckAccessibleClass(String fqn) { |
| String packageName = fqn.substring(0, fqn.lastIndexOf('.')); |
| String simpleName = fqn.substring(fqn.lastIndexOf('.') + 1); |
| StringBuilder checkClassesAccessible = new StringBuilder(); |
| checkClassesAccessible.append("package " + packageName + ";" + |
| "public class " + simpleName + " {" + |
| " public static void runMe() throws Exception {"); |
| for (Entry<String, String> e : MODULES_TO_CHECK_TO_SAMPLE_CLASS.entrySet()) { |
| checkClassesAccessible.append("try {"); |
| checkClassesAccessible.append("Class.forName(\"" + e.getValue() + "\").newInstance();"); |
| checkClassesAccessible.append("System.err.println(\"" + fqn + ":" + e.getKey() + ":true\");"); |
| checkClassesAccessible.append("} catch (Exception ex) {"); |
| checkClassesAccessible.append("System.err.println(\"" + fqn + ":" + e.getKey() + ":false\");"); |
| checkClassesAccessible.append("}"); |
| } |
| |
| checkClassesAccessible.append(" }\n" + |
| "}"); |
| |
| return checkClassesAccessible.toString(); |
| } |
| |
| private static final Map<String, String> MODULES_TO_CHECK_TO_SAMPLE_CLASS = new LinkedHashMap<>(); |
| |
| static { |
| MODULES_TO_CHECK_TO_SAMPLE_CLASS.put("m1x", "api.Api"); |
| MODULES_TO_CHECK_TO_SAMPLE_CLASS.put("m2x", "test.Test"); |
| MODULES_TO_CHECK_TO_SAMPLE_CLASS.put("java.base", "java.lang.Object"); |
| }; |
| |
| @SupportedAnnotationTypes("*") |
| @SupportedOptions("output") |
| public static final class CheckVisibleModule extends AbstractProcessor { |
| |
| @Override |
| public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { |
| String expected = processingEnv.getOptions().get("output"); |
| Set<String> expectedElements = new HashSet<>(Arrays.asList(expected.split(System.getProperty("line.separator")))); |
| Context context = ((JavacProcessingEnvironment) processingEnv).getContext(); |
| Symtab syms = Symtab.instance(context); |
| |
| for (Entry<String, String> e : MODULES_TO_CHECK_TO_SAMPLE_CLASS.entrySet()) { |
| String module = e.getKey(); |
| ModuleElement mod = processingEnv.getElementUtils().getModuleElement(module); |
| String visible = "visible:" + module + ":" + (mod != null); |
| |
| if (!expectedElements.contains(visible)) { |
| throw new AssertionError("actual: " + visible + "; expected: " + expected); |
| } |
| |
| JavacElements javacElements = JavacElements.instance(context); |
| ClassSymbol unnamedClass = javacElements.getTypeElement(syms.unnamedModule, e.getValue()); |
| String unnamed = "cp.CP:" + module + ":" + (unnamedClass != null); |
| |
| if (!expectedElements.contains(unnamed)) { |
| throw new AssertionError("actual: " + unnamed + "; expected: " + expected); |
| } |
| |
| ModuleElement automaticMod = processingEnv.getElementUtils().getModuleElement("automatic"); |
| ClassSymbol automaticClass = javacElements.getTypeElement(automaticMod, e.getValue()); |
| String automatic = "automatic.Automatic:" + module + ":" + (automaticClass != null); |
| |
| if (!expectedElements.contains(automatic)) { |
| throw new AssertionError("actual: " + automatic + "; expected: " + expected); |
| } |
| } |
| |
| return false; |
| } |
| |
| @Override |
| public SourceVersion getSupportedSourceVersion() { |
| return SourceVersion.latest(); |
| } |
| |
| } |
| |
| public String[] augmentOptions(String[] options, List<String> auxOptions, String... baseOptions) { |
| List<String> all = new ArrayList<>(); |
| |
| all.addAll(Arrays.asList(options)); |
| all.addAll(Arrays.asList(baseOptions)); |
| all.addAll(auxOptions); |
| |
| return all.toArray(new String[0]); |
| } |
| |
| private static final String[] MODULE_INFO_VARIANTS = { |
| "module m2x { exports test; }", |
| "module m2x { requires m1x; exports test; }" |
| }; |
| |
| private static final String[][] OPTIONS_VARIANTS = { |
| {"--add-modules", "automatic"}, |
| {"--add-modules", "m1x,automatic"}, |
| {"--add-modules", "jdk.compiler,automatic"}, |
| {"--add-modules", "m1x,jdk.compiler,automatic"}, |
| {"--add-modules", "ALL-SYSTEM,automatic"}, |
| {"--limit-modules", "java.base", "--add-modules", "automatic"}, |
| {"--limit-modules", "java.base", "--add-modules", "ALL-SYSTEM,automatic"}, |
| {"--limit-modules", "m2x", "--add-modules", "automatic"}, |
| {"--limit-modules", "jdk.compiler", "--add-modules", "automatic"}, |
| }; |
| } |