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