1 /* 2 * Copyright (c) 2015, 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 8133884 8162711 8133896 8172158 8172262 8173636 8175119 27 * @summary Verify that annotation processing works. 28 * @library /tools/lib 29 * @modules 30 * jdk.compiler/com.sun.tools.javac.api 31 * jdk.compiler/com.sun.tools.javac.main 32 * @build toolbox.ToolBox toolbox.JavacTask ModuleTestBase 33 * @run main AnnotationProcessing 34 */ 35 36 import java.io.File; 37 import java.io.IOException; 38 import java.io.OutputStream; 39 import java.io.Reader; 40 import java.io.UncheckedIOException; 41 import java.io.Writer; 42 import java.nio.file.Files; 43 import java.nio.file.Path; 44 import java.nio.file.Paths; 45 import java.util.ArrayList; 46 import java.util.Arrays; 47 import java.util.HashMap; 48 import java.util.HashSet; 49 import java.util.List; 50 import java.util.Map; 51 import java.util.Objects; 52 import java.util.Set; 53 import java.util.concurrent.Callable; 54 import java.util.function.Consumer; 55 import java.util.function.Function; 56 import java.util.regex.Pattern; 57 import java.util.stream.Collectors; 58 59 import javax.annotation.processing.AbstractProcessor; 60 import javax.annotation.processing.Filer; 61 import javax.annotation.processing.FilerException; 62 import javax.annotation.processing.Messager; 63 import javax.annotation.processing.ProcessingEnvironment; 64 import javax.annotation.processing.RoundEnvironment; 65 import javax.annotation.processing.SupportedAnnotationTypes; 66 import javax.annotation.processing.SupportedOptions; 67 import javax.lang.model.SourceVersion; 68 import javax.lang.model.element.Element; 69 import javax.lang.model.element.ElementKind; 70 import javax.lang.model.element.ModuleElement; 71 import javax.lang.model.element.ModuleElement.ProvidesDirective; 72 import javax.lang.model.element.ModuleElement.UsesDirective; 73 import javax.lang.model.element.PackageElement; 74 import javax.lang.model.element.TypeElement; 75 import javax.lang.model.element.VariableElement; 76 import javax.lang.model.type.TypeKind; 77 import javax.lang.model.util.ElementFilter; 78 import javax.lang.model.util.ElementScanner9; 79 import javax.tools.Diagnostic.Kind; 80 import javax.tools.FileObject; 81 import javax.tools.JavaCompiler; 82 import javax.tools.JavaCompiler.CompilationTask; 83 import javax.tools.JavaFileManager; 84 import javax.tools.JavaFileManager.Location; 85 import javax.tools.JavaFileObject; 86 import javax.tools.StandardJavaFileManager; 87 import javax.tools.StandardLocation; 88 import javax.tools.ToolProvider; 89 90 import toolbox.JavacTask; 91 import toolbox.Task; 92 import toolbox.Task.Mode; 93 import toolbox.Task.OutputKind; 94 95 public class AnnotationProcessing extends ModuleTestBase { 96 97 public static void main(String... args) throws Exception { 98 new AnnotationProcessing().runTests(); 99 } 100 101 @Test 102 public void testAPSingleModule(Path base) throws Exception { 103 Path moduleSrc = base.resolve("module-src"); 104 Path m1 = moduleSrc.resolve("m1x"); 105 106 Path classes = base.resolve("classes"); 107 108 Files.createDirectories(classes); 109 110 tb.writeJavaFiles(m1, 111 "module m1x { }", 112 "package impl; public class Impl { }"); 113 114 String log = new JavacTask(tb) 115 .options("--module-source-path", moduleSrc.toString(), 116 "-processor", AP.class.getName(), 117 "-AexpectedEnclosedElements=m1x=>impl") 118 .outdir(classes) 119 .files(findJavaFiles(moduleSrc)) 120 .run() 121 .writeAll() 122 .getOutput(Task.OutputKind.DIRECT); 123 124 if (!log.isEmpty()) 125 throw new AssertionError("Unexpected output: " + log); 126 } 127 128 @Test 129 public void testAPMultiModule(Path base) throws Exception { 130 Path moduleSrc = base.resolve("module-src"); 131 Path m1 = moduleSrc.resolve("m1x"); 132 Path m2 = moduleSrc.resolve("m2x"); 133 134 Path classes = base.resolve("classes"); 135 136 Files.createDirectories(classes); 137 138 tb.writeJavaFiles(m1, 139 "module m1x { }", 140 "package impl1; public class Impl1 { }"); 141 142 tb.writeJavaFiles(m2, 143 "module m2x { }", 144 "package impl2; public class Impl2 { }"); 145 146 String log = new JavacTask(tb) 147 .options("--module-source-path", moduleSrc.toString(), 148 "-processor", AP.class.getName(), 149 "-AexpectedEnclosedElements=m1x=>impl1,m2x=>impl2") 150 .outdir(classes) 151 .files(findJavaFiles(moduleSrc)) 152 .run() 153 .writeAll() 154 .getOutput(Task.OutputKind.DIRECT); 155 156 if (!log.isEmpty()) 157 throw new AssertionError("Unexpected output: " + log); 158 } 159 160 @SupportedAnnotationTypes("*") 161 @SupportedOptions("expectedEnclosedElements") 162 public static final class AP extends AbstractProcessor { 163 164 private Map<String, List<String>> module2ExpectedEnclosedElements; 165 private Set<String> seenModules = new HashSet<>(); 166 167 @Override 168 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { 169 if (module2ExpectedEnclosedElements == null) { 170 module2ExpectedEnclosedElements = new HashMap<>(); 171 172 String expectedEnclosedElements = 173 processingEnv.getOptions().get("expectedEnclosedElements"); 174 175 for (String moduleDef : expectedEnclosedElements.split(",")) { 176 String[] module2Packages = moduleDef.split("=>"); 177 178 module2ExpectedEnclosedElements.put(module2Packages[0], 179 List.of(module2Packages[1].split(":"))); 180 } 181 } 182 183 //verify ModuleType and ModuleSymbol behavior: 184 for (Element root : roundEnv.getRootElements()) { 185 ModuleElement module = processingEnv.getElementUtils().getModuleOf(root); 186 187 assertEquals(TypeKind.MODULE, module.asType().getKind()); 188 189 boolean[] seenModule = new boolean[1]; 190 191 module.accept(new ElementScanner9<Void, Void>() { 192 @Override 193 public Void visitModule(ModuleElement e, Void p) { 194 seenModule[0] = true; 195 return null; 196 } 197 @Override 198 public Void scan(Element e, Void p) { 199 throw new AssertionError("Shouldn't get here."); 200 } 201 }, null); 202 203 assertEquals(true, seenModule[0]); 204 205 List<String> actualElements = 206 module.getEnclosedElements() 207 .stream() 208 .map(s -> (PackageElement) s) 209 .map(p -> p.getQualifiedName().toString()) 210 .collect(Collectors.toList()); 211 212 String moduleName = module.getQualifiedName().toString(); 213 214 assertEquals(module2ExpectedEnclosedElements.get(moduleName), 215 actualElements); 216 217 seenModules.add(moduleName); 218 } 219 220 if (roundEnv.processingOver()) { 221 assertEquals(module2ExpectedEnclosedElements.keySet(), seenModules); 222 } 223 224 return false; 225 } 226 227 @Override 228 public SourceVersion getSupportedSourceVersion() { 229 return SourceVersion.latest(); 230 } 231 232 } 233 234 @Test 235 public void testVerifyUsesProvides(Path base) throws Exception { 236 Path moduleSrc = base.resolve("module-src"); 237 Path m1 = moduleSrc.resolve("m1x"); 238 239 Path classes = base.resolve("classes"); 240 241 Files.createDirectories(classes); 242 243 tb.writeJavaFiles(m1, 244 "module m1x { exports api; uses api.Api; provides api.Api with impl.Impl; }", 245 "package api; public class Api { }", 246 "package impl; public class Impl extends api.Api { }"); 247 248 String log = new JavacTask(tb) 249 .options("-doe", "-processor", VerifyUsesProvidesAP.class.getName()) 250 .outdir(classes) 251 .files(findJavaFiles(moduleSrc)) 252 .run() 253 .writeAll() 254 .getOutput(Task.OutputKind.DIRECT); 255 256 if (!log.isEmpty()) 257 throw new AssertionError("Unexpected output: " + log); 258 } 259 260 @SupportedAnnotationTypes("*") 261 public static final class VerifyUsesProvidesAP extends AbstractProcessor { 262 263 @Override 264 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { 265 TypeElement api = processingEnv.getElementUtils().getTypeElement("api.Api"); 266 267 assertNonNull("Cannot find api.Api", api); 268 269 ModuleElement modle = (ModuleElement) processingEnv.getElementUtils().getPackageOf(api).getEnclosingElement(); 270 271 assertNonNull("modle is null", modle); 272 273 List<? extends UsesDirective> uses = ElementFilter.usesIn(modle.getDirectives()); 274 assertEquals(1, uses.size()); 275 assertEquals("api.Api", uses.iterator().next().getService().getQualifiedName().toString()); 276 277 List<? extends ProvidesDirective> provides = ElementFilter.providesIn(modle.getDirectives()); 278 assertEquals(1, provides.size()); 279 assertEquals("api.Api", provides.iterator().next().getService().getQualifiedName().toString()); 280 assertEquals("impl.Impl", provides.iterator().next().getImplementations().get(0).getQualifiedName().toString()); 281 282 return false; 283 } 284 285 @Override 286 public SourceVersion getSupportedSourceVersion() { 287 return SourceVersion.latest(); 288 } 289 290 } 291 292 @Test 293 public void testPackageNoModule(Path base) throws Exception { 294 Path src = base.resolve("src"); 295 Path classes = base.resolve("classes"); 296 297 Files.createDirectories(classes); 298 299 tb.writeJavaFiles(src, 300 "package api; public class Api { }"); 301 302 String log = new JavacTask(tb) 303 .options("-processor", VerifyPackageNoModule.class.getName(), 304 "-source", "8", 305 "-Xlint:-options") 306 .outdir(classes) 307 .files(findJavaFiles(src)) 308 .run() 309 .writeAll() 310 .getOutput(Task.OutputKind.DIRECT); 311 312 if (!log.isEmpty()) 313 throw new AssertionError("Unexpected output: " + log); 314 } 315 316 @SupportedAnnotationTypes("*") 317 public static final class VerifyPackageNoModule extends AbstractProcessor { 318 319 @Override 320 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { 321 TypeElement api = processingEnv.getElementUtils().getTypeElement("api.Api"); 322 323 assertNonNull("Cannot find api.Api", api); 324 325 ModuleElement modle = (ModuleElement) processingEnv.getElementUtils().getPackageOf(api).getEnclosingElement(); 326 327 assertNull("modle is not null", modle); 328 329 return false; 330 } 331 332 @Override 333 public SourceVersion getSupportedSourceVersion() { 334 return SourceVersion.latest(); 335 } 336 337 } 338 339 @Test 340 public void testQualifiedClassForProcessing(Path base) throws Exception { 341 Path moduleSrc = base.resolve("module-src"); 342 Path m1 = moduleSrc.resolve("m1x"); 343 Path m2 = moduleSrc.resolve("m2x"); 344 345 Path classes = base.resolve("classes"); 346 347 Files.createDirectories(classes); 348 349 tb.writeJavaFiles(m1, 350 "module m1x { }", 351 "package impl; public class Impl { int m1x; }"); 352 353 tb.writeJavaFiles(m2, 354 "module m2x { }", 355 "package impl; public class Impl { int m2x; }"); 356 357 new JavacTask(tb) 358 .options("--module-source-path", moduleSrc.toString()) 359 .outdir(classes) 360 .files(findJavaFiles(moduleSrc)) 361 .run() 362 .writeAll() 363 .getOutput(Task.OutputKind.DIRECT); 364 365 List<String> expected = List.of("Note: field: m1x"); 366 367 for (Mode mode : new Mode[] {Mode.API, Mode.CMDLINE}) { 368 List<String> log = new JavacTask(tb, mode) 369 .options("-processor", QualifiedClassForProcessing.class.getName(), 370 "--module-path", classes.toString()) 371 .classes("m1x/impl.Impl") 372 .outdir(classes) 373 .run() 374 .writeAll() 375 .getOutputLines(Task.OutputKind.DIRECT); 376 377 if (!expected.equals(log)) 378 throw new AssertionError("Unexpected output: " + log); 379 } 380 } 381 382 @SupportedAnnotationTypes("*") 383 public static final class QualifiedClassForProcessing extends AbstractProcessor { 384 385 @Override 386 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { 387 if (processingEnv.getElementUtils().getModuleElement("m1x") == null) { 388 throw new AssertionError("No m1x module found."); 389 } 390 391 Messager messager = processingEnv.getMessager(); 392 393 for (TypeElement clazz : ElementFilter.typesIn(roundEnv.getRootElements())) { 394 for (VariableElement field : ElementFilter.fieldsIn(clazz.getEnclosedElements())) { 395 messager.printMessage(Kind.NOTE, "field: " + field.getSimpleName()); 396 } 397 } 398 399 return false; 400 } 401 402 @Override 403 public SourceVersion getSupportedSourceVersion() { 404 return SourceVersion.latest(); 405 } 406 407 } 408 409 @Test 410 public void testModuleInRootElements(Path base) throws Exception { 411 Path moduleSrc = base.resolve("module-src"); 412 Path m1 = moduleSrc.resolve("m1"); 413 414 Path classes = base.resolve("classes"); 415 416 Files.createDirectories(classes); 417 418 tb.writeJavaFiles(m1, 419 "module m1x { exports api; }", 420 "package api; public class Api { }"); 421 422 List<String> log = new JavacTask(tb) 423 .options("-processor", ModuleInRootElementsAP.class.getName()) 424 .outdir(classes) 425 .files(findJavaFiles(moduleSrc)) 426 .run() 427 .writeAll() 428 .getOutputLines(Task.OutputKind.STDERR); 429 430 assertEquals(List.of("module: m1x"), log); 431 } 432 433 @SupportedAnnotationTypes("*") 434 public static final class ModuleInRootElementsAP extends AbstractProcessor { 435 436 @Override 437 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { 438 roundEnv.getRootElements() 439 .stream() 440 .filter(el -> el.getKind() == ElementKind.MODULE) 441 .forEach(mod -> System.err.println("module: " + mod.getSimpleName())); 442 443 return false; 444 } 445 446 @Override 447 public SourceVersion getSupportedSourceVersion() { 448 return SourceVersion.latest(); 449 } 450 451 } 452 453 @Test 454 public void testAnnotationsInModuleInfo(Path base) throws Exception { 455 Path moduleSrc = base.resolve("module-src"); 456 Path m1 = moduleSrc.resolve("m1"); 457 458 tb.writeJavaFiles(m1, 459 "@Deprecated module m1x { }"); 460 461 Path m2 = moduleSrc.resolve("m2x"); 462 463 tb.writeJavaFiles(m2, 464 "@SuppressWarnings(\"\") module m2x { }"); 465 466 Path classes = base.resolve("classes"); 467 468 Files.createDirectories(classes); 469 470 List<String> log = new JavacTask(tb) 471 .options("-processor", AnnotationsInModuleInfoPrint.class.getName()) 472 .outdir(classes) 473 .files(findJavaFiles(m1)) 474 .run() 475 .writeAll() 476 .getOutputLines(Task.OutputKind.DIRECT); 477 478 List<String> expectedLog = List.of("Note: AP Invoked", 479 "Note: AP Invoked"); 480 481 assertEquals(expectedLog, log); 482 483 new JavacTask(tb) 484 .options("-processor", AnnotationsInModuleInfoFail.class.getName()) 485 .outdir(classes) 486 .files(findJavaFiles(m2)) 487 .run() 488 .writeAll(); 489 } 490 491 @SupportedAnnotationTypes("java.lang.Deprecated") 492 public static final class AnnotationsInModuleInfoPrint extends AbstractProcessor { 493 494 @Override 495 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { 496 processingEnv.getMessager().printMessage(Kind.NOTE, "AP Invoked"); 497 return false; 498 } 499 500 @Override 501 public SourceVersion getSupportedSourceVersion() { 502 return SourceVersion.latest(); 503 } 504 505 } 506 507 @SupportedAnnotationTypes("java.lang.Deprecated") 508 public static final class AnnotationsInModuleInfoFail extends AbstractProcessor { 509 510 @Override 511 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { 512 throw new AssertionError(); 513 } 514 515 @Override 516 public SourceVersion getSupportedSourceVersion() { 517 return SourceVersion.latest(); 518 } 519 520 } 521 522 @Test 523 public void testGenerateInMultiModeAPI(Path base) throws Exception { 524 Path moduleSrc = base.resolve("module-src"); 525 Path classes = base.resolve("classes"); 526 527 Files.createDirectories(classes); 528 529 Path m1 = moduleSrc.resolve("m1x"); 530 531 tb.writeJavaFiles(m1, 532 "module m1x { exports api1; }", 533 "package api1; public class Api { }", 534 "package clash; public class C { }"); 535 536 writeFile("1", m1, "api1", "api"); 537 writeFile("2", m1, "clash", "clash"); 538 539 Path m2 = moduleSrc.resolve("m2x"); 540 541 tb.writeJavaFiles(m2, 542 "module m2x { requires m1x; exports api2; }", 543 "package api2; public class Api { }", 544 "package clash; public class C { }"); 545 546 writeFile("3", m2, "api2", "api"); 547 writeFile("4", m2, "clash", "api"); 548 549 //passing testcases: 550 for (String module : Arrays.asList("", "m1x/")) { 551 for (String originating : Arrays.asList("", ", jlObject")) { 552 tb.writeJavaFiles(m1, 553 "package test; class Test { api1.Impl i; }"); 554 555 //source: 556 runCompiler(base, 557 moduleSrc, 558 classes, 559 "createSource(() -> filer.createSourceFile(\"" + module + "api1.Impl\"" + originating + "), \"api1.Impl\", \"package api1; public class Impl {}\")", 560 "--module-source-path", moduleSrc.toString()); 561 assertFileExists(classes, "m1x", "api1", "Impl.class"); 562 563 //class: 564 runCompiler(base, 565 moduleSrc, 566 classes, 567 "createClass(() -> filer.createClassFile(\"" + module + "api1.Impl\"" + originating + "), \"api1.Impl\", \"package api1; public class Impl {}\")", 568 "--module-source-path", moduleSrc.toString()); 569 assertFileExists(classes, "m1x", "api1", "Impl.class"); 570 571 deleteFile(m1.resolve("test").resolve("Test.java")); 572 573 //resource class output: 574 runCompiler(base, 575 moduleSrc, 576 classes, 577 "createSource(() -> filer.createResource(StandardLocation.CLASS_OUTPUT, \"" + module + "api1\", \"impl\"" + originating + "), \"impl\", \"impl\")", 578 "--module-source-path", moduleSrc.toString()); 579 assertFileExists(classes, "m1x", "api1", "impl"); 580 } 581 } 582 583 //get resource module source path: 584 runCompiler(base, 585 m1, 586 classes, 587 "doReadResource(() -> filer.getResource(StandardLocation.MODULE_SOURCE_PATH, \"m1x/api1\", \"api\"), \"1\")", 588 "--module-source-path", moduleSrc.toString()); 589 590 //can generate resources to the single root module: 591 runCompiler(base, 592 m1, 593 classes, 594 "createSource(() -> filer.createResource(StandardLocation.CLASS_OUTPUT, \"m1x/impl\", \"impl\"), \"impl\", \"impl\")", 595 "--module-source-path", moduleSrc.toString()); 596 assertFileExists(classes, "m1x", "impl", "impl"); 597 598 //check --default-module-for-created-files option: 599 for (String pack : Arrays.asList("clash", "doesnotexist")) { 600 tb.writeJavaFiles(m1, 601 "package test; class Test { " + pack + ".Pass i; }"); 602 runCompiler(base, 603 moduleSrc, 604 classes, 605 "createSource(() -> filer.createSourceFile(\"" + pack + ".Pass\")," + 606 " \"" + pack + ".Pass\"," + 607 " \"package " + pack + ";" + 608 " public class Pass { }\")", 609 "--module-source-path", moduleSrc.toString(), 610 "--default-module-for-created-files=m1x"); 611 assertFileExists(classes, "m1x", pack, "Pass.class"); 612 assertFileNotExists(classes, "m2x", pack, "Pass.class"); 613 614 runCompiler(base, 615 moduleSrc, 616 classes, 617 "createClass(() -> filer.createClassFile(\"" + pack + ".Pass\")," + 618 " \"" + pack + ".Pass\"," + 619 " \"package " + pack + ";" + 620 " public class Pass { }\")", 621 "--module-source-path", moduleSrc.toString(), 622 "--default-module-for-created-files=m1x"); 623 assertFileExists(classes, "m1x", pack, "Pass.class"); 624 assertFileNotExists(classes, "m2x", pack, "Pass.class"); 625 626 deleteFile(m1.resolve("test").resolve("Test.java")); 627 628 runCompiler(base, 629 moduleSrc, 630 classes, 631 "createSource(() -> filer.createResource(StandardLocation.CLASS_OUTPUT," + 632 " \"" + pack + "\", \"impl\"), \"impl\", \"impl\")", 633 "--module-source-path", moduleSrc.toString(), 634 "--default-module-for-created-files=m1x"); 635 assertFileExists(classes, "m1x", pack, "impl"); 636 assertFileNotExists(classes, "m2x", pack, "impl"); 637 638 runCompiler(base, 639 moduleSrc, 640 classes, 641 "doReadResource(() -> filer.getResource(StandardLocation.CLASS_OUTPUT," + 642 " \"" + pack + "\", \"resource\"), \"1\")", 643 p -> writeFile("1", p.resolve("m1x"), pack, "resource"), 644 "--module-source-path", moduleSrc.toString(), 645 "--default-module-for-created-files=m1x"); 646 } 647 648 //wrong default module: 649 runCompiler(base, 650 moduleSrc, 651 classes, 652 "expectFilerException(() -> filer.createResource(StandardLocation.CLASS_OUTPUT," + 653 " \"clash\", \"impl\"))", 654 "--module-source-path", moduleSrc.toString(), 655 "--default-module-for-created-files=doesnotexist"); 656 657 String[] failingCases = { 658 //must not generate to unnamed package: 659 "expectFilerException(() -> filer.createSourceFile(\"Fail\"))", 660 "expectFilerException(() -> filer.createClassFile(\"Fail\"))", 661 "expectFilerException(() -> filer.createSourceFile(\"m1x/Fail\"))", 662 "expectFilerException(() -> filer.createClassFile(\"m1x/Fail\"))", 663 664 //cannot infer module name, package clash: 665 "expectFilerException(() -> filer.createSourceFile(\"clash.Fail\"))", 666 "expectFilerException(() -> filer.createClassFile(\"clash.Fail\"))", 667 "expectFilerException(() -> filer.createResource(StandardLocation.CLASS_OUTPUT, \"clash\", \"impl\"))", 668 "expectFilerException(() -> filer.getResource(StandardLocation.CLASS_OUTPUT, \"clash\", \"impl\"))", 669 670 //cannot infer module name, package does not exist: 671 "expectFilerException(() -> filer.createSourceFile(\"doesnotexist.Fail\"))", 672 "expectFilerException(() -> filer.createClassFile(\"doesnotexist.Fail\"))", 673 "expectFilerException(() -> filer.createResource(StandardLocation.CLASS_OUTPUT, \"doesnotexist\", \"impl\"))", 674 "expectFilerException(() -> filer.getResource(StandardLocation.CLASS_OUTPUT, \"doesnotexist\", \"impl\"))", 675 676 //cannot generate sources/classes to modules that are not root modules: 677 "expectFilerException(() -> filer.createSourceFile(\"java.base/fail.Fail\"))", 678 "expectFilerException(() -> filer.createClassFile(\"java.base/fail.Fail\"))", 679 680 //cannot read from module locations if module not given and not inferable: 681 "expectFilerException(() -> filer.getResource(StandardLocation.SYSTEM_MODULES, \"fail\", \"Fail\"))", 682 683 //wrong module given: 684 "expectException(() -> filer.getResource(StandardLocation.SYSTEM_MODULES, \"java.compiler/java.lang\", \"Object.class\"))", 685 }; 686 687 for (String failingCode : failingCases) { 688 System.err.println("failing code: " + failingCode); 689 runCompiler(base, 690 moduleSrc, 691 classes, 692 failingCode, 693 "--module-source-path", moduleSrc.toString()); 694 } 695 } 696 697 private void deleteFile(Path file) throws IOException { 698 long startTime = System.currentTimeMillis(); 699 700 do { 701 Files.delete(file); 702 if (!Files.exists(file)) { 703 return; 704 } 705 System.err.println("!! File not deleted !!"); 706 System.gc(); // allow finalizers and cleaners to run 707 try { 708 Thread.sleep(RETRY_DELETE_MILLIS); 709 } catch (InterruptedException e) { 710 throw new IOException("Interrupted while deleting " + file, e); 711 } 712 } while ((System.currentTimeMillis() - startTime) <= MAX_RETRY_DELETE_MILLIS); 713 714 throw new IOException("Can't delete " + file); 715 } 716 717 718 private static final int RETRY_DELETE_MILLIS; 719 private static final int MAX_RETRY_DELETE_MILLIS; 720 721 static { 722 boolean isWindows = System.getProperty("os.name").startsWith("Windows"); 723 RETRY_DELETE_MILLIS = isWindows ? 500 : 0; 724 MAX_RETRY_DELETE_MILLIS = isWindows ? 15 * 1000 : 0; 725 } 726 727 public static abstract class GeneratingAP extends AbstractProcessor { 728 729 public void createSource(CreateFileObject file, String name, String content) { 730 try (Writer out = file.create().openWriter()) { 731 out.write(content); 732 } catch (IOException ex) { 733 throw new IllegalStateException(ex); 734 } 735 } 736 737 public void createClass(CreateFileObject file, String name, String content) { 738 String fileNameStub = name.replace(".", File.separator); 739 740 try (OutputStream out = file.create().openOutputStream()) { 741 Path scratch = Files.createDirectories(Paths.get("")); 742 Path scratchSrc = scratch.resolve(fileNameStub + ".java").toAbsolutePath(); 743 744 Files.createDirectories(scratchSrc.getParent()); 745 746 try (Writer w = Files.newBufferedWriter(scratchSrc)) { 747 w.write(content); 748 } 749 750 Path scratchClasses = scratch.resolve("classes"); 751 752 Files.createDirectories(scratchClasses); 753 754 JavaCompiler comp = ToolProvider.getSystemJavaCompiler(); 755 try (StandardJavaFileManager fm = comp.getStandardFileManager(null, null, null)) { 756 List<String> options = List.of("-d", scratchClasses.toString()); 757 Iterable<? extends JavaFileObject> files = fm.getJavaFileObjects(scratchSrc); 758 CompilationTask task = comp.getTask(null, fm, null, options, null, files); 759 760 if (!task.call()) { 761 throw new AssertionError("compilation failed"); 762 } 763 } 764 765 Path classfile = scratchClasses.resolve(fileNameStub + ".class"); 766 767 Files.copy(classfile, out); 768 } catch (IOException ex) { 769 throw new IllegalStateException(ex); 770 } 771 } 772 773 public void doReadResource(CreateFileObject file, String expectedContent) { 774 try { 775 StringBuilder actualContent = new StringBuilder(); 776 777 try (Reader r = file.create().openReader(true)) { 778 int read; 779 780 while ((read = r.read()) != (-1)) { 781 actualContent.append((char) read); 782 } 783 784 } 785 786 assertEquals(expectedContent, actualContent.toString()); 787 } catch (IOException ex) { 788 throw new IllegalStateException(ex); 789 } 790 } 791 792 public void checkResourceExists(CreateFileObject file) { 793 try { 794 file.create().openInputStream().close(); 795 } catch (IOException ex) { 796 throw new IllegalStateException(ex); 797 } 798 } 799 800 public interface CreateFileObject { 801 public FileObject create() throws IOException; 802 } 803 804 public void expectFilerException(Callable<Object> c) { 805 try { 806 c.call(); 807 throw new AssertionError("Expected exception not thrown"); 808 } catch (FilerException ex) { 809 //expected 810 } catch (Exception ex) { 811 throw new IllegalStateException(ex); 812 } 813 } 814 815 public void expectException(Callable<Object> c) { 816 try { 817 c.call(); 818 throw new AssertionError("Expected exception not thrown"); 819 } catch (IOException ex) { 820 //expected 821 } catch (Exception ex) { 822 throw new IllegalStateException(ex); 823 } 824 } 825 826 @Override 827 public SourceVersion getSupportedSourceVersion() { 828 return SourceVersion.latest(); 829 } 830 831 } 832 833 @Test 834 public void testGenerateSingleModule(Path base) throws Exception { 835 Path classes = base.resolve("classes"); 836 837 Files.createDirectories(classes); 838 839 Path src = base.resolve("module-src"); 840 Path m1 = src.resolve("m1x"); 841 842 tb.writeJavaFiles(m1, 843 "module m1x { }", 844 "package test; class Test { impl.Impl i; }"); 845 Path m2 = src.resolve("m2x"); 846 847 tb.writeJavaFiles(m2, 848 "module m2x { }"); 849 850 for (String[] options : new String[][] {new String[] {"-sourcepath", m1.toString()}, 851 new String[] {"--module-source-path", src.toString()}}) { 852 String modulePath = options[0].equals("--module-source-path") ? "m1x" : ""; 853 //passing testcases: 854 for (String module : Arrays.asList("", "m1x/")) { 855 for (String originating : Arrays.asList("", ", jlObject")) { 856 tb.writeJavaFiles(m1, 857 "package test; class Test { impl.Impl i; }"); 858 859 //source: 860 runCompiler(base, 861 m1, 862 classes, 863 "createSource(() -> filer.createSourceFile(\"" + module + "impl.Impl\"" + originating + "), \"impl.Impl\", \"package impl; public class Impl {}\")", 864 options); 865 assertFileExists(classes, modulePath, "impl", "Impl.class"); 866 867 //class: 868 runCompiler(base, 869 m1, 870 classes, 871 "createClass(() -> filer.createClassFile(\"" + module + "impl.Impl\"" + originating + "), \"impl.Impl\", \"package impl; public class Impl {}\")", 872 options); 873 assertFileExists(classes, modulePath, "impl", "Impl.class"); 874 875 deleteFile(m1.resolve("test").resolve("Test.java")); 876 877 //resource class output: 878 runCompiler(base, 879 m1, 880 classes, 881 "createSource(() -> filer.createResource(StandardLocation.CLASS_OUTPUT, \"impl\", \"impl\"" + originating + "), \"impl\", \"impl\")", 882 options); 883 assertFileExists(classes, modulePath, "impl", "impl"); 884 } 885 } 886 } 887 888 //get resource source path: 889 writeFile("1", m1, "impl", "resource"); 890 runCompiler(base, 891 m1, 892 classes, 893 "doReadResource(() -> filer.getResource(StandardLocation.SOURCE_PATH, \"impl\", \"resource\"), \"1\")", 894 "-sourcepath", m1.toString()); 895 //must not specify module when reading non-module oriented locations: 896 runCompiler(base, 897 m1, 898 classes, 899 "expectFilerException(() -> filer.getResource(StandardLocation.SOURCE_PATH, \"m1x/impl\", \"resource\"))", 900 "-sourcepath", m1.toString()); 901 902 deleteFile(m1.resolve("impl").resolve("resource")); 903 904 //can read resources from the system module path if module name given: 905 runCompiler(base, 906 m1, 907 classes, 908 "checkResourceExists(() -> filer.getResource(StandardLocation.SYSTEM_MODULES, \"java.base/java.lang\", \"Object.class\"))", 909 "-sourcepath", m1.toString()); 910 911 //can read resources from the system module path if module inferable: 912 runCompiler(base, 913 m1, 914 classes, 915 "expectFilerException(() -> filer.getResource(StandardLocation.SYSTEM_MODULES, \"java.lang\", \"Object.class\"))", 916 "-sourcepath", m1.toString()); 917 918 //cannot generate resources to modules that are not root modules: 919 runCompiler(base, 920 m1, 921 classes, 922 "expectFilerException(() -> filer.createResource(StandardLocation.CLASS_OUTPUT, \"java.base/fail\", \"Fail\"))", 923 "--module-source-path", src.toString()); 924 925 //can generate resources to the single root module: 926 runCompiler(base, 927 m1, 928 classes, 929 "createSource(() -> filer.createResource(StandardLocation.CLASS_OUTPUT, \"impl\", \"impl\"), \"impl\", \"impl\")", 930 "--module-source-path", src.toString()); 931 assertFileExists(classes, "m1x", "impl", "impl"); 932 933 String[] failingCases = { 934 //must not generate to unnamed package: 935 "expectFilerException(() -> filer.createSourceFile(\"Fail\"))", 936 "expectFilerException(() -> filer.createClassFile(\"Fail\"))", 937 "expectFilerException(() -> filer.createSourceFile(\"m1x/Fail\"))", 938 "expectFilerException(() -> filer.createClassFile(\"m1x/Fail\"))", 939 940 //cannot generate sources/classes to modules that are not root modules: 941 "expectFilerException(() -> filer.createSourceFile(\"java.base/fail.Fail\"))", 942 "expectFilerException(() -> filer.createClassFile(\"java.base/fail.Fail\"))", 943 944 //cannot specify module name for class output when not in the multi-module mode: 945 "expectFilerException(() -> filer.createResource(StandardLocation.CLASS_OUTPUT, \"m1x/fail\", \"Fail\"))", 946 947 //cannot read from module locations if module not given: 948 "expectFilerException(() -> filer.getResource(StandardLocation.SYSTEM_MODULES, \"fail\", \"Fail\"))", 949 950 //wrong module given: 951 "expectException(() -> filer.getResource(StandardLocation.SYSTEM_MODULES, \"java.compiler/java.lang\", \"Object.class\"))", 952 }; 953 954 for (String failingCode : failingCases) { 955 System.err.println("failing code: " + failingCode); 956 runCompiler(base, 957 m1, 958 classes, 959 failingCode, 960 "-sourcepath", m1.toString()); 961 } 962 963 deleteFile(m1.resolve("module-info.java")); 964 tb.writeJavaFiles(m1, 965 "package test; class Test { }"); 966 967 runCompiler(base, 968 m1, 969 classes, 970 "expectFilerException(() -> filer.createSourceFile(\"m1x/impl.Impl\"))", 971 "-sourcepath", m1.toString(), 972 "-source", "8"); 973 974 runCompiler(base, 975 m1, 976 classes, 977 "expectFilerException(() -> filer.createClassFile(\"m1x/impl.Impl\"))", 978 "-sourcepath", m1.toString(), 979 "-source", "8"); 980 } 981 982 private void runCompiler(Path base, Path src, Path classes, 983 String code, String... options) throws IOException { 984 runCompiler(base, src, classes, code, p -> {}, options); 985 } 986 987 private void runCompiler(Path base, Path src, Path classes, 988 String code, Consumer<Path> generateToClasses, 989 String... options) throws IOException { 990 Path apClasses = base.resolve("ap-classes"); 991 if (Files.exists(apClasses)) { 992 tb.cleanDirectory(apClasses); 993 } else { 994 Files.createDirectories(apClasses); 995 } 996 compileAP(apClasses, code); 997 if (Files.exists(classes)) { 998 tb.cleanDirectory(classes); 999 } else { 1000 Files.createDirectories(classes); 1001 } 1002 generateToClasses.accept(classes); 1003 List<String> opts = new ArrayList<>(); 1004 opts.addAll(Arrays.asList(options)); 1005 opts.add("-processorpath"); 1006 opts.add(System.getProperty("test.class.path") + File.pathSeparator + apClasses.toString()); 1007 opts.add("-processor"); 1008 opts.add("AP"); 1009 new JavacTask(tb) 1010 .options(opts) 1011 .outdir(classes) 1012 .files(findJavaFiles(src)) 1013 .run() 1014 .writeAll(); 1015 } 1016 1017 private void compileAP(Path target, String code) { 1018 String processorCode = 1019 "import java.util.*;\n" + 1020 "import javax.annotation.processing.*;\n" + 1021 "import javax.lang.model.*;\n" + 1022 "import javax.lang.model.element.*;\n" + 1023 "import javax.lang.model.type.*;\n" + 1024 "import javax.lang.model.util.*;\n" + 1025 "import javax.tools.*;\n" + 1026 "@SupportedAnnotationTypes(\"*\")\n" + 1027 "public final class AP extends AnnotationProcessing.GeneratingAP {\n" + 1028 "\n" + 1029 " int round;\n" + 1030 "\n" + 1031 " @Override\n" + 1032 " public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {\n" + 1033 " if (round++ != 0)\n" + 1034 " return false;\n" + 1035 " Filer filer = processingEnv.getFiler();\n" + 1036 " TypeElement jlObject = processingEnv.getElementUtils().getTypeElement(\"java.lang.Object\");\n" + 1037 code + ";\n" + 1038 " return false;\n" + 1039 " }\n" + 1040 " }\n"; 1041 new JavacTask(tb) 1042 .options("-classpath", System.getProperty("test.class.path")) 1043 .sources(processorCode) 1044 .outdir(target) 1045 .run() 1046 .writeAll(); 1047 } 1048 1049 @Test 1050 public void testGenerateInUnnamedModeAPI(Path base) throws Exception { 1051 Path classes = base.resolve("classes"); 1052 1053 Files.createDirectories(classes); 1054 1055 Path src = base.resolve("src"); 1056 1057 tb.writeJavaFiles(src, 1058 "class T {}"); 1059 1060 new JavacTask(tb) 1061 .options("-processor", UnnamedModeAPITestAP.class.getName(), 1062 "-sourcepath", src.toString()) 1063 .outdir(classes) 1064 .files(findJavaFiles(src)) 1065 .run() 1066 .writeAll(); 1067 1068 assertFileExists(classes, "Impl1.class"); 1069 assertFileExists(classes, "Impl2.class"); 1070 } 1071 1072 @Test 1073 public void testGenerateInNoModeAPI(Path base) throws Exception { 1074 Path classes = base.resolve("classes"); 1075 1076 Files.createDirectories(classes); 1077 1078 Path src = base.resolve("src"); 1079 1080 tb.writeJavaFiles(src, 1081 "class T {}"); 1082 1083 new JavacTask(tb) 1084 .options("-processor", UnnamedModeAPITestAP.class.getName(), 1085 "-source", "8", "-target", "8", 1086 "-sourcepath", src.toString()) 1087 .outdir(classes) 1088 .files(findJavaFiles(src)) 1089 .run() 1090 .writeAll(); 1091 1092 assertFileExists(classes, "Impl1.class"); 1093 assertFileExists(classes, "Impl2.class"); 1094 } 1095 1096 @SupportedAnnotationTypes("*") 1097 public static final class UnnamedModeAPITestAP extends GeneratingAP { 1098 1099 int round; 1100 1101 @Override 1102 public synchronized void init(ProcessingEnvironment processingEnv) { 1103 super.init(processingEnv); 1104 } 1105 1106 @Override 1107 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { 1108 if (round++ != 0) 1109 return false; 1110 1111 Filer filer = processingEnv.getFiler(); 1112 1113 //must not generate to unnamed package: 1114 createSource(() -> filer.createSourceFile("Impl1"), "Impl1", "class Impl1 {}"); 1115 createClass(() -> filer.createClassFile("Impl2"), "Impl2", "class Impl2 {}"); 1116 1117 return false; 1118 } 1119 1120 } 1121 1122 @Test 1123 public void testDisambiguateAnnotations(Path base) throws Exception { 1124 Path classes = base.resolve("classes"); 1125 1126 Files.createDirectories(classes); 1127 1128 Path src = base.resolve("src"); 1129 Path m1 = src.resolve("m1x"); 1130 1131 tb.writeJavaFiles(m1, 1132 "module m1x { exports api; }", 1133 "package api; public @interface A {}", 1134 "package api; public @interface B {}"); 1135 1136 Path m2 = src.resolve("m2x"); 1137 1138 tb.writeJavaFiles(m2, 1139 "module m2x { exports api; }", 1140 "package api; public @interface A {}", 1141 "package api; public @interface B {}"); 1142 1143 Path m3 = src.resolve("m3x"); 1144 1145 tb.writeJavaFiles(m3, 1146 "module m3x { requires m1x; }", 1147 "package impl; import api.*; @A @B public class T {}"); 1148 1149 Path m4 = src.resolve("m4x"); 1150 1151 tb.writeJavaFiles(m4, 1152 "module m4x { requires m2x; }", 1153 "package impl; import api.*; @A @B public class T {}"); 1154 1155 List<String> log; 1156 List<String> expected; 1157 1158 log = new JavacTask(tb) 1159 .options("-processor", SelectAnnotationATestAP.class.getName() + "," + SelectAnnotationBTestAP.class.getName(), 1160 "--module-source-path", src.toString(), 1161 "-m", "m1x,m2x") 1162 .outdir(classes) 1163 .run() 1164 .writeAll() 1165 .getOutputLines(OutputKind.STDERR); 1166 1167 expected = List.of(""); 1168 1169 if (!expected.equals(log)) { 1170 throw new AssertionError("Output does not match; output: " + log); 1171 } 1172 1173 log = new JavacTask(tb) 1174 .options("-processor", SelectAnnotationATestAP.class.getName() + "," + SelectAnnotationBTestAP.class.getName(), 1175 "--module-source-path", src.toString(), 1176 "-m", "m3x") 1177 .outdir(classes) 1178 .run() 1179 .writeAll() 1180 .getOutputLines(OutputKind.STDERR); 1181 1182 expected = List.of("SelectAnnotationBTestAP", 1183 "SelectAnnotationBTestAP"); 1184 1185 if (!expected.equals(log)) { 1186 throw new AssertionError("Output does not match; output: " + log); 1187 } 1188 1189 log = new JavacTask(tb) 1190 .options("-processor", SelectAnnotationATestAP.class.getName() + "," + 1191 SelectAnnotationBTestAP.class.getName() + "," + 1192 SelectAnnotationAStrictTestAP.class.getName(), 1193 "--module-source-path", src.toString(), 1194 "-m", "m4x") 1195 .outdir(classes) 1196 .run() 1197 .writeAll() 1198 .getOutputLines(OutputKind.STDERR); 1199 1200 expected = List.of("SelectAnnotationATestAP", 1201 "SelectAnnotationBTestAP", 1202 "SelectAnnotationAStrictTestAP", 1203 "SelectAnnotationATestAP", 1204 "SelectAnnotationBTestAP", 1205 "SelectAnnotationAStrictTestAP"); 1206 1207 if (!expected.equals(log)) { 1208 throw new AssertionError("Output does not match; output: " + log); 1209 } 1210 } 1211 1212 @Test 1213 public void testDisambiguateAnnotationsUnnamedModule(Path base) throws Exception { 1214 Path classes = base.resolve("classes"); 1215 1216 Files.createDirectories(classes); 1217 1218 Path src = base.resolve("src"); 1219 1220 tb.writeJavaFiles(src, 1221 "package api; public @interface A {}", 1222 "package api; public @interface B {}", 1223 "package impl; import api.*; @A @B public class T {}"); 1224 1225 List<String> log = new JavacTask(tb) 1226 .options("-processor", SelectAnnotationATestAP.class.getName() + "," + 1227 SelectAnnotationBTestAP.class.getName() + "," + 1228 SelectAnnotationAStrictTestAP.class.getName()) 1229 .outdir(classes) 1230 .files(findJavaFiles(src)) 1231 .run() 1232 .writeAll() 1233 .getOutputLines(OutputKind.STDERR); 1234 1235 List<String> expected = List.of("SelectAnnotationBTestAP", 1236 "SelectAnnotationBTestAP"); 1237 1238 if (!expected.equals(log)) { 1239 throw new AssertionError("Output does not match; output: " + log); 1240 } 1241 } 1242 1243 @Test 1244 public void testDisambiguateAnnotationsNoModules(Path base) throws Exception { 1245 Path classes = base.resolve("classes"); 1246 1247 Files.createDirectories(classes); 1248 1249 Path src = base.resolve("src"); 1250 1251 tb.writeJavaFiles(src, 1252 "package api; public @interface A {}", 1253 "package api; public @interface B {}", 1254 "package impl; import api.*; @A @B public class T {}"); 1255 1256 List<String> log = new JavacTask(tb) 1257 .options("-processor", SelectAnnotationATestAP.class.getName() + "," + 1258 SelectAnnotationBTestAP.class.getName() + "," + 1259 SelectAnnotationAStrictTestAP.class.getName(), 1260 "-source", "8", "-target", "8") 1261 .outdir(classes) 1262 .files(findJavaFiles(src)) 1263 .run() 1264 .writeAll() 1265 .getOutputLines(OutputKind.STDERR); 1266 1267 List<String> expected = List.of("SelectAnnotationATestAP", 1268 "SelectAnnotationBTestAP", 1269 "SelectAnnotationATestAP", 1270 "SelectAnnotationBTestAP"); 1271 1272 if (!expected.equals(log)) { 1273 throw new AssertionError("Output does not match; output: " + log); 1274 } 1275 } 1276 1277 @SupportedAnnotationTypes("m2x/api.A") 1278 public static final class SelectAnnotationATestAP extends AbstractProcessor { 1279 1280 @Override 1281 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { 1282 System.err.println("SelectAnnotationATestAP"); 1283 1284 return false; 1285 } 1286 1287 } 1288 1289 @SupportedAnnotationTypes("api.B") 1290 public static final class SelectAnnotationBTestAP extends AbstractProcessor { 1291 1292 @Override 1293 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { 1294 System.err.println("SelectAnnotationBTestAP"); 1295 1296 return false; 1297 } 1298 1299 } 1300 1301 public static final class SelectAnnotationAStrictTestAP extends AbstractProcessor { 1302 1303 @Override 1304 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { 1305 System.err.println("SelectAnnotationAStrictTestAP"); 1306 1307 return false; 1308 } 1309 1310 @Override 1311 public Set<String> getSupportedAnnotationTypes() { 1312 return Set.of("m2x/api.A"); 1313 } 1314 1315 } 1316 1317 private static void writeFile(String content, Path base, String... pathElements) { 1318 try { 1319 Path file = resolveFile(base, pathElements); 1320 1321 Files.createDirectories(file.getParent()); 1322 1323 try (Writer out = Files.newBufferedWriter(file)) { 1324 out.append(content); 1325 } 1326 } catch (IOException ex) { 1327 throw new UncheckedIOException(ex); 1328 } 1329 } 1330 1331 @Test 1332 public void testUnboundLookup(Path base) throws Exception { 1333 Path src = base.resolve("src"); 1334 1335 tb.writeJavaFiles(src, 1336 "package impl.conflict.src; public class Impl { }"); 1337 1338 Path moduleSrc = base.resolve("module-src"); 1339 Path m1 = moduleSrc.resolve("m1x"); 1340 Path m2 = moduleSrc.resolve("m2x"); 1341 1342 Path classes = base.resolve("classes"); 1343 Path cpClasses = base.resolve("cpClasses"); 1344 1345 Files.createDirectories(classes); 1346 Files.createDirectories(cpClasses); 1347 1348 tb.writeJavaFiles(m1, 1349 "module m1x { }", 1350 "package impl1; public class Impl { }", 1351 "package impl.conflict.module; class Impl { }", 1352 "package impl.conflict.clazz; public class pkg { public static class I { } }", 1353 "package impl.conflict.src; public class Impl { }", 1354 "package nested.pack.pack; public class Impl { }", 1355 "package unique.nested; public class Impl { }"); 1356 1357 tb.writeJavaFiles(m2, 1358 "module m2x { }", 1359 "package impl2; public class Impl { }", 1360 "package impl.conflict.module; class Impl { }", 1361 "package impl.conflict; public class clazz { public static class pkg { } }", 1362 "package nested.pack; public class Impl { }"); 1363 1364 //from source: 1365 String log = new JavacTask(tb) 1366 .options("--module-source-path", moduleSrc.toString(), 1367 "--source-path", src.toString(), 1368 "-processorpath", System.getProperty("test.class.path"), 1369 "-processor", UnboundLookup.class.getName(), 1370 "-XDrawDiagnostics") 1371 .outdir(classes) 1372 .files(findJavaFiles(moduleSrc)) 1373 .run() 1374 .writeAll() 1375 .getOutput(OutputKind.DIRECT); 1376 1377 String moduleImplConflictString = 1378 "- compiler.note.multiple.elements: getTypeElement, impl.conflict.module.Impl, m2x, m1x"; 1379 String srcConflictString = 1380 "- compiler.note.multiple.elements: getTypeElement, impl.conflict.src.Impl, m1x, unnamed module"; 1381 1382 if (!log.contains(moduleImplConflictString) || 1383 !log.contains(srcConflictString)) { 1384 throw new AssertionError("Expected output not found: " + log); 1385 } 1386 1387 if (log.split(Pattern.quote(moduleImplConflictString)).length > 2) { 1388 throw new AssertionError("Too many warnings in: " + log); 1389 } 1390 1391 new JavacTask(tb) 1392 .options("--source-path", src.toString()) 1393 .outdir(cpClasses) 1394 .files(findJavaFiles(src)) 1395 .run() 1396 .writeAll(); 1397 1398 //from classfiles: 1399 new JavacTask(tb) 1400 .options("--module-path", classes.toString(), 1401 "--class-path", cpClasses.toString(), 1402 "--add-modules", "m1x,m2x", 1403 "-processorpath", System.getProperty("test.class.path"), 1404 "-processor", UnboundLookup.class.getName(), 1405 "-proc:only") 1406 .classes("java.lang.Object") 1407 .run() 1408 .writeAll(); 1409 1410 //source 8: 1411 new JavacTask(tb) 1412 .options("--source-path", src.toString(), 1413 "-source", "8", 1414 "-processorpath", System.getProperty("test.class.path"), 1415 "-processor", UnboundLookup8.class.getName()) 1416 .outdir(cpClasses) 1417 .files(findJavaFiles(src)) 1418 .run() 1419 .writeAll(); 1420 1421 } 1422 1423 @SupportedAnnotationTypes("*") 1424 public static final class UnboundLookup extends AbstractProcessor { 1425 1426 @Override 1427 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { 1428 assertTypeElementExists("impl1.Impl", "m1x"); 1429 assertPackageElementExists("impl1", "m1x"); 1430 assertTypeElementExists("impl2.Impl", "m2x"); 1431 assertTypeElementExists("impl.conflict.clazz.pkg.I", "m1x"); 1432 assertTypeElementExists("impl.conflict.clazz", "m2x"); 1433 assertPackageElementExists("impl.conflict.clazz", "m1x"); 1434 assertPackageElementExists("impl2", "m2x"); 1435 assertPackageElementExists("nested.pack.pack", "m1x"); 1436 assertPackageElementExists("nested.pack", "m2x"); 1437 assertTypeElementExists("unique.nested.Impl", "m1x"); 1438 assertTypeElementNotFound("impl.conflict.module.Impl"); 1439 assertTypeElementNotFound("impl.conflict.module.Impl"); //check that the warning/note is produced only once 1440 assertPackageElementNotFound("impl.conflict.module"); 1441 assertTypeElementNotFound("impl.conflict.src.Impl"); 1442 assertPackageElementNotFound("impl.conflict.src"); 1443 assertTypeElementNotFound("impl.conflict.clazz.pkg"); 1444 assertPackageElementNotFound("unique"); //do not return packages without members in module mode 1445 assertTypeElementNotFound("nested"); //cannot distinguish between m1x and m2x 1446 1447 return false; 1448 } 1449 1450 private void assertTypeElementExists(String name, String expectedModule) { 1451 assertElementExists(name, "class", processingEnv.getElementUtils() :: getTypeElement, expectedModule); 1452 } 1453 1454 private void assertPackageElementExists(String name, String expectedModule) { 1455 assertElementExists(name, "package", processingEnv.getElementUtils() :: getPackageElement, expectedModule); 1456 } 1457 1458 private void assertElementExists(String name, String type, Function<String, Element> getter, String expectedModule) { 1459 Element clazz = getter.apply(name); 1460 1461 if (clazz == null) { 1462 throw new AssertionError("No " + name + " " + type + " found."); 1463 } 1464 1465 ModuleElement mod = processingEnv.getElementUtils().getModuleOf(clazz); 1466 1467 if (!mod.getQualifiedName().contentEquals(expectedModule)) { 1468 throw new AssertionError(name + " found in an unexpected module: " + mod.getQualifiedName()); 1469 } 1470 } 1471 1472 private void assertTypeElementNotFound(String name) { 1473 assertElementNotFound(name, processingEnv.getElementUtils() :: getTypeElement); 1474 } 1475 1476 private void assertPackageElementNotFound(String name) { 1477 assertElementNotFound(name, processingEnv.getElementUtils() :: getPackageElement); 1478 } 1479 1480 private void assertElementNotFound(String name, Function<String, Element> getter) { 1481 Element found = getter.apply(name); 1482 1483 if (found != null) { 1484 fail("Element found unexpectedly: " + found); 1485 } 1486 } 1487 1488 @Override 1489 public SourceVersion getSupportedSourceVersion() { 1490 return SourceVersion.latest(); 1491 } 1492 1493 } 1494 1495 @SupportedAnnotationTypes("*") 1496 public static final class UnboundLookup8 extends AbstractProcessor { 1497 1498 @Override 1499 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { 1500 if (processingEnv.getElementUtils().getTypeElement("impl.conflict.src.Impl") == null) { 1501 throw new AssertionError("impl.conflict.src.Impl."); 1502 } 1503 1504 if (processingEnv.getElementUtils().getModuleElement("java.base") != null) { 1505 throw new AssertionError("getModuleElement != null for -source 8"); 1506 } 1507 1508 return false; 1509 } 1510 1511 @Override 1512 public SourceVersion getSupportedSourceVersion() { 1513 return SourceVersion.latest(); 1514 } 1515 1516 } 1517 1518 @Test 1519 public void testWrongDefaultTargetModule(Path base) throws Exception { 1520 Path src = base.resolve("src"); 1521 1522 tb.writeJavaFiles(src, 1523 "package test; public class Test { }"); 1524 1525 Path classes = base.resolve("classes"); 1526 1527 Files.createDirectories(classes); 1528 1529 List<String> log = new JavacTask(tb) 1530 .options("--default-module-for-created-files=m!", 1531 "-XDrawDiagnostics") 1532 .outdir(classes) 1533 .files(findJavaFiles(src)) 1534 .run(Task.Expect.FAIL) 1535 .writeAll() 1536 .getOutputLines(OutputKind.DIRECT); 1537 1538 List<String> expected = Arrays.asList( 1539 "- compiler.err.bad.name.for.option: --default-module-for-created-files, m!" 1540 ); 1541 1542 if (!log.equals(expected)) { 1543 throw new AssertionError("Expected output not found."); 1544 } 1545 } 1546 1547 private static void assertNonNull(String msg, Object val) { 1548 if (val == null) { 1549 throw new AssertionError(msg); 1550 } 1551 } 1552 1553 private static void assertNull(String msg, Object val) { 1554 if (val != null) { 1555 throw new AssertionError(msg); 1556 } 1557 } 1558 1559 private static void assertEquals(Object expected, Object actual) { 1560 if (!Objects.equals(expected, actual)) { 1561 throw new AssertionError("expected: " + expected + "; actual=" + actual); 1562 } 1563 } 1564 1565 private static void assertFileExists(Path base, String... pathElements) { 1566 Path file = resolveFile(base, pathElements); 1567 1568 if (!Files.exists(file)) { 1569 throw new AssertionError("Expected file: " + file + " exist, but it does not."); 1570 } 1571 } 1572 1573 private static void assertFileNotExists(Path base, String... pathElements) { 1574 Path file = resolveFile(base, pathElements); 1575 1576 if (Files.exists(file)) { 1577 throw new AssertionError("Expected file: " + file + " exist, but it does not."); 1578 } 1579 } 1580 1581 static Path resolveFile(Path base, String... pathElements) { 1582 Path file = base; 1583 1584 for (String el : pathElements) { 1585 file = file.resolve(el); 1586 } 1587 1588 return file; 1589 } 1590 1591 private static void fail(String msg) { 1592 throw new AssertionError(msg); 1593 } 1594 1595 }