1 /*
   2  * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 /**
  25  * @test
  26  * @bug 8142968 8154956 8170987 8171412
  27  * @summary Test --add-modules and --limit-modules; also test the "enabled" modules.
  28  * @library /tools/lib
  29  * @modules
  30  *      jdk.compiler/com.sun.tools.javac.api
  31  *      jdk.compiler/com.sun.tools.javac.code
  32  *      jdk.compiler/com.sun.tools.javac.main
  33  *      jdk.compiler/com.sun.tools.javac.model
  34  *      jdk.compiler/com.sun.tools.javac.processing
  35  *      jdk.compiler/com.sun.tools.javac.util
  36  *      jdk.jdeps/com.sun.tools.javap
  37  * @build toolbox.ToolBox toolbox.JarTask toolbox.JavacTask toolbox.JavaTask ModuleTestBase
  38  * @run main AddLimitMods
  39  */
  40 
  41 import java.io.File;
  42 import java.nio.file.Files;
  43 import java.nio.file.Path;
  44 import java.util.AbstractMap.SimpleEntry;
  45 import java.util.ArrayList;
  46 import java.util.Arrays;
  47 import java.util.Collections;
  48 import java.util.HashSet;
  49 import java.util.LinkedHashMap;
  50 import java.util.List;
  51 import java.util.Map;
  52 import java.util.Map.Entry;
  53 import java.util.Objects;
  54 import java.util.Set;
  55 
  56 import javax.annotation.processing.AbstractProcessor;
  57 import javax.annotation.processing.RoundEnvironment;
  58 import javax.annotation.processing.SupportedAnnotationTypes;
  59 import javax.annotation.processing.SupportedOptions;
  60 import javax.lang.model.SourceVersion;
  61 import javax.lang.model.element.ModuleElement;
  62 import javax.lang.model.element.TypeElement;
  63 
  64 import com.sun.tools.javac.code.Symbol.ClassSymbol;
  65 import com.sun.tools.javac.code.Symtab;
  66 import com.sun.tools.javac.model.JavacElements;
  67 import com.sun.tools.javac.processing.JavacProcessingEnvironment;
  68 import com.sun.tools.javac.util.Context;
  69 
  70 import toolbox.JarTask;
  71 import toolbox.JavacTask;
  72 import toolbox.JavaTask;
  73 import toolbox.Task;
  74 
  75 public class AddLimitMods extends ModuleTestBase {
  76 
  77     public static void main(String... args) throws Exception {
  78         AddLimitMods t = new AddLimitMods();
  79         t.runTests();
  80     }
  81 
  82     @Test
  83     public void testManual(Path base) throws Exception {
  84         Path moduleSrc = base.resolve("module-src");
  85         Path m1 = moduleSrc.resolve("m1x");
  86 
  87         tb.writeJavaFiles(m1,
  88                           "module m1x { requires m2x; requires m3x; }");
  89 
  90         Path m2 = moduleSrc.resolve("m2x");
  91 
  92         tb.writeJavaFiles(m2,
  93                           "module m2x { requires m3x; exports m2x; }",
  94                           "package m2x; public class M2 {}");
  95 
  96         Path m3 = moduleSrc.resolve("m3x");
  97 
  98         tb.writeJavaFiles(m3,
  99                           "module m3x { exports m3x; }",
 100                           "package m3x; public class M3 {}");
 101 
 102         Path modulePath = base.resolve("module-path");
 103 
 104         Files.createDirectories(modulePath);
 105 
 106         new JavacTask(tb)
 107                 .options("--module-source-path", moduleSrc.toString())
 108                 .outdir(modulePath)
 109                 .files(findJavaFiles(m3))
 110                 .run()
 111                 .writeAll();
 112 
 113         new JavacTask(tb)
 114                 .options("--module-source-path", moduleSrc.toString())
 115                 .outdir(modulePath)
 116                 .files(findJavaFiles(m2))
 117                 .run()
 118                 .writeAll();
 119 
 120         //real test
 121         new JavacTask(tb)
 122                 .options("--module-path", modulePath.toString(),
 123                          "--should-stop=ifNoError=FLOW",
 124                          "--limit-modules", "java.base")
 125                 .outdir(modulePath)
 126                 .files(findJavaFiles(m1))
 127                 .run(Task.Expect.FAIL)
 128                 .writeAll();
 129 
 130         new JavacTask(tb)
 131                 .options("--module-path", modulePath.toString(),
 132                          "--should-stop=ifNoError=FLOW",
 133                          "--limit-modules", "java.base",
 134                          "--add-modules", "m2x")
 135                 .outdir(modulePath)
 136                 .files(findJavaFiles(m1))
 137                 .run(Task.Expect.FAIL)
 138                 .writeAll();
 139 
 140         new JavacTask(tb)
 141                 .options("--module-path", modulePath.toString(),
 142                          "--should-stop=ifNoError=FLOW",
 143                          "--limit-modules", "java.base",
 144                          "--add-modules", "m2x,m3x")
 145                 .outdir(modulePath)
 146                 .files(findJavaFiles(m1))
 147                 .run()
 148                 .writeAll();
 149 
 150         new JavacTask(tb)
 151                 .options("--module-path", modulePath.toString(),
 152                          "--should-stop=ifNoError=FLOW",
 153                          "--limit-modules", "m2x")
 154                 .outdir(modulePath)
 155                 .files(findJavaFiles(m1))
 156                 .run()
 157                 .writeAll();
 158 
 159         new JavacTask(tb)
 160                 .options("--module-path", modulePath.toString(),
 161                          "--should-stop=ifNoError=FLOW",
 162                          "--limit-modules", "m3x")
 163                 .outdir(modulePath)
 164                 .files(findJavaFiles(m1))
 165                 .run(Task.Expect.FAIL)
 166                 .writeAll();
 167 
 168         new JavacTask(tb)
 169                 .options("--module-path", modulePath.toString(),
 170                          "--should-stop=ifNoError=FLOW",
 171                          "--limit-modules", "m3x",
 172                          "--add-modules", "m2x")
 173                 .outdir(modulePath)
 174                 .files(findJavaFiles(m1))
 175                 .run()
 176                 .writeAll();
 177     }
 178 
 179     @Test
 180     public void testAllModulePath(Path base) throws Exception {
 181         if (Files.isDirectory(base))
 182             tb.cleanDirectory(base);
 183 
 184         Path moduleSrc = base.resolve("module-src");
 185         Path m1 = moduleSrc.resolve("m1x");
 186 
 187         tb.writeJavaFiles(m1,
 188                           "module m1x { exports api; }",
 189                           "package api; public class Api { }");
 190 
 191         Path modulePath = base.resolve("module-path");
 192 
 193         Files.createDirectories(modulePath);
 194 
 195         new JavacTask(tb)
 196                 .options("--module-source-path", moduleSrc.toString())
 197                 .outdir(modulePath)
 198                 .files(findJavaFiles(moduleSrc))
 199                 .run()
 200                 .writeAll();
 201 
 202         Path cpSrc = base.resolve("cp-src");
 203         tb.writeJavaFiles(cpSrc, "package test; public class Test { api.Api api; }");
 204 
 205         Path cpOut = base.resolve("cp-out");
 206 
 207         Files.createDirectories(cpOut);
 208 
 209         new JavacTask(tb)
 210                 .options("--module-path", modulePath.toString())
 211                 .outdir(cpOut)
 212                 .files(findJavaFiles(cpSrc))
 213                 .run(Task.Expect.FAIL)
 214                 .writeAll();
 215 
 216         new JavacTask(tb)
 217                 .options("--module-path", modulePath.toString(),
 218                          "--add-modules", "ALL-MODULE-PATH")
 219                 .outdir(cpOut)
 220                 .files(findJavaFiles(cpSrc))
 221                 .run()
 222                 .writeAll();
 223 
 224         List<String> actual;
 225         List<String> expected = Arrays.asList(
 226                 "- compiler.err.addmods.all.module.path.invalid",
 227                 "1 error");
 228 
 229         actual = new JavacTask(tb)
 230                    .options("--module-source-path", moduleSrc.toString(),
 231                             "-XDrawDiagnostics",
 232                             "--add-modules", "ALL-MODULE-PATH")
 233                    .outdir(modulePath)
 234                    .files(findJavaFiles(moduleSrc))
 235                    .run(Task.Expect.FAIL)
 236                    .writeAll()
 237                    .getOutputLines(Task.OutputKind.DIRECT);
 238 
 239         if (!Objects.equals(actual, expected)) {
 240             throw new IllegalStateException("incorrect errors; actual=" + actual + "; expected=" + expected);
 241         }
 242 
 243         actual = new JavacTask(tb)
 244                    .options("--patch-module", "java.base=" + cpSrc.toString(),
 245                             "-XDrawDiagnostics",
 246                             "--add-modules", "ALL-MODULE-PATH")
 247                    .outdir(cpOut)
 248                    .files(findJavaFiles(cpSrc))
 249                    .run(Task.Expect.FAIL)
 250                    .writeAll()
 251                    .getOutputLines(Task.OutputKind.DIRECT);
 252 
 253         if (!Objects.equals(actual, expected)) {
 254             throw new IllegalStateException("incorrect errors; actual=" + actual + "; expected=" + expected);
 255         }
 256 
 257         actual = new JavacTask(tb, Task.Mode.CMDLINE)
 258                    .options("-source", "8", "-target", "8",
 259                             "-XDrawDiagnostics",
 260                             "--add-modules", "ALL-MODULE-PATH")
 261                    .outdir(cpOut)
 262                    .files(findJavaFiles(cpSrc))
 263                    .run(Task.Expect.FAIL)
 264                    .writeAll()
 265                    .getOutputLines(Task.OutputKind.DIRECT);
 266 
 267         if (!actual.contains("- compiler.err.option.not.allowed.with.target: --add-modules, 8")) {
 268             throw new IllegalStateException("incorrect errors; actual=" + actual);
 269         }
 270 
 271         tb.writeJavaFiles(cpSrc, "module m1x {}");
 272 
 273         actual = new JavacTask(tb)
 274                    .options("-XDrawDiagnostics",
 275                             "--add-modules", "ALL-MODULE-PATH")
 276                    .outdir(cpOut)
 277                    .files(findJavaFiles(cpSrc))
 278                    .run(Task.Expect.FAIL)
 279                    .writeAll()
 280                    .getOutputLines(Task.OutputKind.DIRECT);
 281 
 282         if (!Objects.equals(actual, expected)) {
 283             throw new IllegalStateException("incorrect errors; actual=" + actual + "; expected=" + expected);
 284         }
 285     }
 286 
 287     @Test
 288     public void testRuntime2Compile(Path base) throws Exception {
 289         Path classpathSrc = base.resolve("classpath-src");
 290         Path classpathOut = base.resolve("classpath-out");
 291 
 292         tb.writeJavaFiles(classpathSrc,
 293                           generateCheckAccessibleClass("cp.CP"));
 294 
 295         Files.createDirectories(classpathOut);
 296 
 297         System.err.println("Compiling classpath-src files:");
 298         new JavacTask(tb)
 299                 .outdir(classpathOut)
 300                 .files(findJavaFiles(classpathSrc))
 301                 .run()
 302                 .writeAll()
 303                 .getOutput(Task.OutputKind.DIRECT);
 304 
 305         Path automaticSrc = base.resolve("automatic-src");
 306         Path automaticOut = base.resolve("automatic-out");
 307 
 308         tb.writeJavaFiles(automaticSrc,
 309                           generateCheckAccessibleClass("automatic.Automatic"));
 310 
 311         Files.createDirectories(automaticOut);
 312 
 313         System.err.println("Compiling automatic-src files:");
 314         new JavacTask(tb)
 315                 .outdir(automaticOut)
 316                 .files(findJavaFiles(automaticSrc))
 317                 .run()
 318                 .writeAll()
 319                 .getOutput(Task.OutputKind.DIRECT);
 320 
 321         Path modulePath = base.resolve("module-path");
 322 
 323         Files.createDirectories(modulePath);
 324 
 325         Path automaticJar = modulePath.resolve("automatic.jar");
 326 
 327         System.err.println("Creating automatic.jar:");
 328         new JarTask(tb, automaticJar)
 329           .baseDir(automaticOut)
 330           .files("automatic/Automatic.class")
 331           .run();
 332 
 333         Path moduleSrc = base.resolve("module-src");
 334         Path m1 = moduleSrc.resolve("m1x");
 335 
 336         tb.writeJavaFiles(m1,
 337                           "module m1x { exports api; }",
 338                           "package api; public class Api { public void test() { } }");
 339 
 340         System.err.println("Compiling module-src files:");
 341         new JavacTask(tb)
 342                 .options("--module-source-path", moduleSrc.toString())
 343                 .outdir(modulePath)
 344                 .files(findJavaFiles(moduleSrc))
 345                 .run()
 346                 .writeAll()
 347                 .getOutput(Task.OutputKind.DIRECT);
 348 
 349         int index = 0;
 350 
 351         for (String moduleInfo : MODULE_INFO_VARIANTS) {
 352             for (String[] options : OPTIONS_VARIANTS) {
 353                 index++;
 354 
 355                 System.err.println("Running check: " + moduleInfo + "; " + Arrays.asList(options));
 356 
 357                 Path m2Runtime = base.resolve(index + "-runtime").resolve("m2x");
 358                 Path out = base.resolve(index + "-runtime").resolve("out").resolve("m2x");
 359 
 360                 Files.createDirectories(out);
 361 
 362                 StringBuilder testClassNamed = new StringBuilder();
 363 
 364                 testClassNamed.append("package test;\n" +
 365                                       "public class Test {\n" +
 366                                       "    public static void main(String... args) throws Exception {\n");
 367 
 368                 for (Entry<String, String> e : MODULES_TO_CHECK_TO_SAMPLE_CLASS.entrySet()) {
 369                     testClassNamed.append("        System.err.println(\"visible:" + e.getKey() + ":\" + ModuleLayer.boot().findModule(\"" + e.getKey() + "\").isPresent());\n");
 370                 }
 371 
 372                 testClassNamed.append("        Class<?> cp = Class.forName(Test.class.getClassLoader().getUnnamedModule(), \"cp.CP\");\n");
 373                 testClassNamed.append("        cp.getDeclaredMethod(\"runMe\").invoke(null);\n");
 374 
 375                 testClassNamed.append("        Class<?> automatic = Class.forName(ModuleLayer.boot().findModule(\"automatic\").get(), \"automatic.Automatic\");\n");
 376                 testClassNamed.append("        automatic.getDeclaredMethod(\"runMe\").invoke(null);\n");
 377 
 378                 testClassNamed.append("    }\n" +
 379                                       "}");
 380 
 381                 tb.writeJavaFiles(m2Runtime, moduleInfo, testClassNamed.toString());
 382 
 383                 System.err.println("Compiling " + m2Runtime + " files:");
 384                 new JavacTask(tb)
 385                    .options("--module-path", modulePath.toString())
 386                    .outdir(out)
 387                    .files(findJavaFiles(m2Runtime))
 388                    .run()
 389                    .writeAll();
 390 
 391                 boolean success;
 392                 String output;
 393 
 394                 try {
 395                     System.err.println("Running m2x/test.Test:");
 396                     output = new JavaTask(tb)
 397                        .vmOptions(augmentOptions(options,
 398                                                  Collections.emptyList(),
 399                                                  "--module-path", modulePath.toString() + File.pathSeparator + out.getParent().toString(),
 400                                                  "--class-path", classpathOut.toString(),
 401                                                  "--add-reads", "m2x=ALL-UNNAMED,automatic",
 402                                                  "-m", "m2x/test.Test"))
 403                        .run()
 404                        .writeAll()
 405                        .getOutput(Task.OutputKind.STDERR);
 406 
 407                     success = true;
 408                 } catch (Task.TaskError err) {
 409                     success = false;
 410                     output = "";
 411                 }
 412 
 413                 Path m2 = base.resolve(String.valueOf(index)).resolve("m2x");
 414 
 415                 tb.writeJavaFiles(m2,
 416                                   moduleInfo,
 417                                   "package test;\n" +
 418                                   "public class Test {}\n");
 419 
 420                 List<String> auxOptions = success ? Arrays.asList(
 421                     "--processor-path", System.getProperty("test.class.path"),
 422                     "-processor", CheckVisibleModule.class.getName(),
 423                     "-Aoutput=" + output,
 424                     "-XDaccessInternalAPI=true"
 425                 ) : Collections.emptyList();
 426 
 427                 System.err.println("Compiling/processing m2x files:");
 428                 new JavacTask(tb)
 429                    .options(augmentOptions(options,
 430                                            auxOptions,
 431                                            "--module-path", modulePath.toString(),
 432                                            "--class-path", classpathOut.toString(),
 433                                            "--should-stop=ifNoError=FLOW"))
 434                    .outdir(modulePath)
 435                    .files(findJavaFiles(m2))
 436                    .run(success ? Task.Expect.SUCCESS : Task.Expect.FAIL)
 437                    .writeAll();
 438             }
 439         }
 440     }
 441 
 442     private String generateCheckAccessibleClass(String fqn) {
 443         String packageName = fqn.substring(0, fqn.lastIndexOf('.'));
 444         String simpleName = fqn.substring(fqn.lastIndexOf('.') + 1);
 445         StringBuilder checkClassesAccessible = new StringBuilder();
 446         checkClassesAccessible.append("package " + packageName + ";" +
 447                                       "public class " + simpleName + " {" +
 448                                       "    public static void runMe() throws Exception {");
 449         for (Entry<String, String> e : MODULES_TO_CHECK_TO_SAMPLE_CLASS.entrySet()) {
 450             checkClassesAccessible.append("try {");
 451             checkClassesAccessible.append("Class.forName(\"" + e.getValue() + "\").newInstance();");
 452             checkClassesAccessible.append("System.err.println(\"" + fqn + ":" + e.getKey() + ":true\");");
 453             checkClassesAccessible.append("} catch (Exception ex) {");
 454             checkClassesAccessible.append("System.err.println(\"" + fqn + ":" + e.getKey() + ":false\");");
 455             checkClassesAccessible.append("}");
 456         }
 457 
 458         checkClassesAccessible.append("    }\n" +
 459                                       "}");
 460 
 461         return checkClassesAccessible.toString();
 462     }
 463 
 464     private static final Map<String, String> MODULES_TO_CHECK_TO_SAMPLE_CLASS = new LinkedHashMap<>();
 465 
 466     static {
 467         MODULES_TO_CHECK_TO_SAMPLE_CLASS.put("m1x", "api.Api");
 468         MODULES_TO_CHECK_TO_SAMPLE_CLASS.put("m2x", "test.Test");
 469         MODULES_TO_CHECK_TO_SAMPLE_CLASS.put("java.base", "java.lang.Object");
 470     };
 471 
 472     @SupportedAnnotationTypes("*")
 473     @SupportedOptions("output")
 474     public static final class CheckVisibleModule extends AbstractProcessor {
 475 
 476         @Override
 477         public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
 478             String expected = processingEnv.getOptions().get("output");
 479             Set<String> expectedElements = new HashSet<>(Arrays.asList(expected.split(System.getProperty("line.separator"))));
 480             Context context = ((JavacProcessingEnvironment) processingEnv).getContext();
 481             Symtab syms = Symtab.instance(context);
 482 
 483             for (Entry<String, String> e : MODULES_TO_CHECK_TO_SAMPLE_CLASS.entrySet()) {
 484                 String module = e.getKey();
 485                 ModuleElement mod = processingEnv.getElementUtils().getModuleElement(module);
 486                 String visible = "visible:" + module + ":" + (mod != null);
 487 
 488                 if (!expectedElements.contains(visible)) {
 489                     throw new AssertionError("actual: " + visible + "; expected: " + expected);
 490                 }
 491 
 492                 JavacElements javacElements = JavacElements.instance(context);
 493                 ClassSymbol unnamedClass = javacElements.getTypeElement(syms.unnamedModule, e.getValue());
 494                 String unnamed = "cp.CP:" + module + ":" + (unnamedClass != null);
 495 
 496                 if (!expectedElements.contains(unnamed)) {
 497                     throw new AssertionError("actual: " + unnamed + "; expected: " + expected);
 498                 }
 499 
 500                 ModuleElement automaticMod = processingEnv.getElementUtils().getModuleElement("automatic");
 501                 ClassSymbol automaticClass = javacElements.getTypeElement(automaticMod, e.getValue());
 502                 String automatic = "automatic.Automatic:" + module + ":" + (automaticClass != null);
 503 
 504                 if (!expectedElements.contains(automatic)) {
 505                     throw new AssertionError("actual: " + automatic + "; expected: " + expected);
 506                 }
 507             }
 508 
 509             return false;
 510         }
 511 
 512         @Override
 513         public SourceVersion getSupportedSourceVersion() {
 514             return SourceVersion.latest();
 515         }
 516 
 517     }
 518 
 519     public String[] augmentOptions(String[] options, List<String> auxOptions, String... baseOptions) {
 520         List<String> all = new ArrayList<>();
 521 
 522         all.addAll(Arrays.asList(options));
 523         all.addAll(Arrays.asList(baseOptions));
 524         all.addAll(auxOptions);
 525 
 526         return all.toArray(new String[0]);
 527     }
 528 
 529     private static final String[] MODULE_INFO_VARIANTS = {
 530         "module m2x { exports test; }",
 531         "module m2x { requires m1x; exports test; }"
 532     };
 533 
 534     private static final String[][] OPTIONS_VARIANTS = {
 535         {"--add-modules", "automatic"},
 536         {"--add-modules", "m1x,automatic"},
 537         {"--add-modules", "jdk.compiler,automatic"},
 538         {"--add-modules", "m1x,jdk.compiler,automatic"},
 539         {"--add-modules", "ALL-SYSTEM,automatic"},
 540         {"--limit-modules", "java.base", "--add-modules", "automatic"},
 541         {"--limit-modules", "java.base", "--add-modules", "ALL-SYSTEM,automatic"},
 542         {"--limit-modules", "m2x", "--add-modules", "automatic"},
 543         {"--limit-modules", "jdk.compiler", "--add-modules", "automatic"},
 544     };
 545 }