1/* 2 * Copyright (c) 2015, 2017, 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 36import java.io.File; 37import java.io.IOException; 38import java.io.OutputStream; 39import java.io.Reader; 40import java.io.UncheckedIOException; 41import java.io.Writer; 42import java.nio.file.Files; 43import java.nio.file.Path; 44import java.nio.file.Paths; 45import java.util.ArrayList; 46import java.util.Arrays; 47import java.util.HashMap; 48import java.util.HashSet; 49import java.util.List; 50import java.util.Map; 51import java.util.Objects; 52import java.util.Set; 53import java.util.concurrent.Callable; 54import java.util.function.Consumer; 55import java.util.function.Function; 56import java.util.regex.Pattern; 57import java.util.stream.Collectors; 58 59import javax.annotation.processing.AbstractProcessor; 60import javax.annotation.processing.Filer; 61import javax.annotation.processing.FilerException; 62import javax.annotation.processing.Messager; 63import javax.annotation.processing.ProcessingEnvironment; 64import javax.annotation.processing.RoundEnvironment; 65import javax.annotation.processing.SupportedAnnotationTypes; 66import javax.annotation.processing.SupportedOptions; 67import javax.lang.model.SourceVersion; 68import javax.lang.model.element.Element; 69import javax.lang.model.element.ElementKind; 70import javax.lang.model.element.ModuleElement; 71import javax.lang.model.element.ModuleElement.ProvidesDirective; 72import javax.lang.model.element.ModuleElement.UsesDirective; 73import javax.lang.model.element.PackageElement; 74import javax.lang.model.element.TypeElement; 75import javax.lang.model.element.VariableElement; 76import javax.lang.model.type.TypeKind; 77import javax.lang.model.util.ElementFilter; 78import javax.lang.model.util.ElementScanner9; 79import javax.tools.Diagnostic.Kind; 80import javax.tools.FileObject; 81import javax.tools.JavaCompiler; 82import javax.tools.JavaCompiler.CompilationTask; 83import javax.tools.JavaFileManager; 84import javax.tools.JavaFileManager.Location; 85import javax.tools.JavaFileObject; 86import javax.tools.StandardJavaFileManager; 87import javax.tools.StandardLocation; 88import javax.tools.ToolProvider; 89 90import toolbox.JavacTask; 91import toolbox.Task; 92import toolbox.Task.Mode; 93import toolbox.Task.OutputKind; 94 95public 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 Files.delete(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 Files.delete(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 public static abstract class GeneratingAP extends AbstractProcessor { 698 699 public void createSource(CreateFileObject file, String name, String content) { 700 try (Writer out = file.create().openWriter()) { 701 out.write(content); 702 } catch (IOException ex) { 703 throw new IllegalStateException(ex); 704 } 705 } 706 707 public void createClass(CreateFileObject file, String name, String content) { 708 String fileNameStub = name.replace(".", File.separator); 709 710 try (OutputStream out = file.create().openOutputStream()) { 711 Path scratch = Files.createDirectories(Paths.get("")); 712 Path scratchSrc = scratch.resolve(fileNameStub + ".java").toAbsolutePath(); 713 714 Files.createDirectories(scratchSrc.getParent()); 715 716 try (Writer w = Files.newBufferedWriter(scratchSrc)) { 717 w.write(content); 718 } 719 720 Path scratchClasses = scratch.resolve("classes"); 721 722 Files.createDirectories(scratchClasses); 723 724 JavaCompiler comp = ToolProvider.getSystemJavaCompiler(); 725 try (StandardJavaFileManager fm = comp.getStandardFileManager(null, null, null)) { 726 List<String> options = List.of("-d", scratchClasses.toString()); 727 Iterable<? extends JavaFileObject> files = fm.getJavaFileObjects(scratchSrc); 728 CompilationTask task = comp.getTask(null, fm, null, options, null, files); 729 730 if (!task.call()) { 731 throw new AssertionError("compilation failed"); 732 } 733 } 734 735 Path classfile = scratchClasses.resolve(fileNameStub + ".class"); 736 737 Files.copy(classfile, out); 738 } catch (IOException ex) { 739 throw new IllegalStateException(ex); 740 } 741 } 742 743 public void doReadResource(CreateFileObject file, String expectedContent) { 744 try { 745 StringBuilder actualContent = new StringBuilder(); 746 747 try (Reader r = file.create().openReader(true)) { 748 int read; 749 750 while ((read = r.read()) != (-1)) { 751 actualContent.append((char) read); 752 } 753 754 } 755 756 assertEquals(expectedContent, actualContent.toString()); 757 } catch (IOException ex) { 758 throw new IllegalStateException(ex); 759 } 760 } 761 762 public void checkResourceExists(CreateFileObject file) { 763 try { 764 file.create().openInputStream().close(); 765 } catch (IOException ex) { 766 throw new IllegalStateException(ex); 767 } 768 } 769 770 public interface CreateFileObject { 771 public FileObject create() throws IOException; 772 } 773 774 public void expectFilerException(Callable<Object> c) { 775 try { 776 c.call(); 777 throw new AssertionError("Expected exception not thrown"); 778 } catch (FilerException ex) { 779 //expected 780 } catch (Exception ex) { 781 throw new IllegalStateException(ex); 782 } 783 } 784 785 public void expectException(Callable<Object> c) { 786 try { 787 c.call(); 788 throw new AssertionError("Expected exception not thrown"); 789 } catch (IOException ex) { 790 //expected 791 } catch (Exception ex) { 792 throw new IllegalStateException(ex); 793 } 794 } 795 796 @Override 797 public SourceVersion getSupportedSourceVersion() { 798 return SourceVersion.latest(); 799 } 800 801 } 802 803 @Test 804 public void testGenerateSingleModule(Path base) throws Exception { 805 Path classes = base.resolve("classes"); 806 807 Files.createDirectories(classes); 808 809 Path src = base.resolve("module-src"); 810 Path m1 = src.resolve("m1x"); 811 812 tb.writeJavaFiles(m1, 813 "module m1x { }", 814 "package test; class Test { impl.Impl i; }"); 815 Path m2 = src.resolve("m2x"); 816 817 tb.writeJavaFiles(m2, 818 "module m2x { }"); 819 820 for (String[] options : new String[][] {new String[] {"-sourcepath", m1.toString()}, 821 new String[] {"--module-source-path", src.toString()}}) { 822 String modulePath = options[0].equals("--module-source-path") ? "m1x" : ""; 823 //passing testcases: 824 for (String module : Arrays.asList("", "m1x/")) { 825 for (String originating : Arrays.asList("", ", jlObject")) { 826 tb.writeJavaFiles(m1, 827 "package test; class Test { impl.Impl i; }"); 828 829 //source: 830 runCompiler(base, 831 m1, 832 classes, 833 "createSource(() -> filer.createSourceFile(\"" + module + "impl.Impl\"" + originating + "), \"impl.Impl\", \"package impl; public class Impl {}\")", 834 options); 835 assertFileExists(classes, modulePath, "impl", "Impl.class"); 836 837 //class: 838 runCompiler(base, 839 m1, 840 classes, 841 "createClass(() -> filer.createClassFile(\"" + module + "impl.Impl\"" + originating + "), \"impl.Impl\", \"package impl; public class Impl {}\")", 842 options); 843 assertFileExists(classes, modulePath, "impl", "Impl.class"); 844 845 Files.delete(m1.resolve("test").resolve("Test.java")); 846 847 //resource class output: 848 runCompiler(base, 849 m1, 850 classes, 851 "createSource(() -> filer.createResource(StandardLocation.CLASS_OUTPUT, \"impl\", \"impl\"" + originating + "), \"impl\", \"impl\")", 852 options); 853 assertFileExists(classes, modulePath, "impl", "impl"); 854 } 855 } 856 } 857 858 //get resource source path: 859 writeFile("1", m1, "impl", "resource"); 860 runCompiler(base, 861 m1, 862 classes, 863 "doReadResource(() -> filer.getResource(StandardLocation.SOURCE_PATH, \"impl\", \"resource\"), \"1\")", 864 "-sourcepath", m1.toString()); 865 //must not specify module when reading non-module oriented locations: 866 runCompiler(base, 867 m1, 868 classes, 869 "expectFilerException(() -> filer.getResource(StandardLocation.SOURCE_PATH, \"m1x/impl\", \"resource\"))", 870 "-sourcepath", m1.toString()); 871 872 Files.delete(m1.resolve("impl").resolve("resource")); 873 874 //can read resources from the system module path if module name given: 875 runCompiler(base, 876 m1, 877 classes, 878 "checkResourceExists(() -> filer.getResource(StandardLocation.SYSTEM_MODULES, \"java.base/java.lang\", \"Object.class\"))", 879 "-sourcepath", m1.toString()); 880 881 //can read resources from the system module path if module inferable: 882 runCompiler(base, 883 m1, 884 classes, 885 "expectFilerException(() -> filer.getResource(StandardLocation.SYSTEM_MODULES, \"java.lang\", \"Object.class\"))", 886 "-sourcepath", m1.toString()); 887 888 //cannot generate resources to modules that are not root modules: 889 runCompiler(base, 890 m1, 891 classes, 892 "expectFilerException(() -> filer.createResource(StandardLocation.CLASS_OUTPUT, \"java.base/fail\", \"Fail\"))", 893 "--module-source-path", src.toString()); 894 895 //can generate resources to the single root module: 896 runCompiler(base, 897 m1, 898 classes, 899 "createSource(() -> filer.createResource(StandardLocation.CLASS_OUTPUT, \"impl\", \"impl\"), \"impl\", \"impl\")", 900 "--module-source-path", src.toString()); 901 assertFileExists(classes, "m1x", "impl", "impl"); 902 903 String[] failingCases = { 904 //must not generate to unnamed package: 905 "expectFilerException(() -> filer.createSourceFile(\"Fail\"))", 906 "expectFilerException(() -> filer.createClassFile(\"Fail\"))", 907 "expectFilerException(() -> filer.createSourceFile(\"m1x/Fail\"))", 908 "expectFilerException(() -> filer.createClassFile(\"m1x/Fail\"))", 909 910 //cannot generate sources/classes to modules that are not root modules: 911 "expectFilerException(() -> filer.createSourceFile(\"java.base/fail.Fail\"))", 912 "expectFilerException(() -> filer.createClassFile(\"java.base/fail.Fail\"))", 913 914 //cannot specify module name for class output when not in the multi-module mode: 915 "expectFilerException(() -> filer.createResource(StandardLocation.CLASS_OUTPUT, \"m1x/fail\", \"Fail\"))", 916 917 //cannot read from module locations if module not given: 918 "expectFilerException(() -> filer.getResource(StandardLocation.SYSTEM_MODULES, \"fail\", \"Fail\"))", 919 920 //wrong module given: 921 "expectException(() -> filer.getResource(StandardLocation.SYSTEM_MODULES, \"java.compiler/java.lang\", \"Object.class\"))", 922 }; 923 924 for (String failingCode : failingCases) { 925 System.err.println("failing code: " + failingCode); 926 runCompiler(base, 927 m1, 928 classes, 929 failingCode, 930 "-sourcepath", m1.toString()); 931 } 932 933 Files.delete(m1.resolve("module-info.java")); 934 tb.writeJavaFiles(m1, 935 "package test; class Test { }"); 936 937 runCompiler(base, 938 m1, 939 classes, 940 "expectFilerException(() -> filer.createSourceFile(\"m1x/impl.Impl\"))", 941 "-sourcepath", m1.toString(), 942 "-source", "8"); 943 944 runCompiler(base, 945 m1, 946 classes, 947 "expectFilerException(() -> filer.createClassFile(\"m1x/impl.Impl\"))", 948 "-sourcepath", m1.toString(), 949 "-source", "8"); 950 } 951 952 private void runCompiler(Path base, Path src, Path classes, 953 String code, String... options) throws IOException { 954 runCompiler(base, src, classes, code, p -> {}, options); 955 } 956 957 private void runCompiler(Path base, Path src, Path classes, 958 String code, Consumer<Path> generateToClasses, 959 String... options) throws IOException { 960 Path apClasses = base.resolve("ap-classes"); 961 if (Files.exists(apClasses)) { 962 tb.cleanDirectory(apClasses); 963 } else { 964 Files.createDirectories(apClasses); 965 } 966 compileAP(apClasses, code); 967 if (Files.exists(classes)) { 968 tb.cleanDirectory(classes); 969 } else { 970 Files.createDirectories(classes); 971 } 972 generateToClasses.accept(classes); 973 List<String> opts = new ArrayList<>(); 974 opts.addAll(Arrays.asList(options)); 975 opts.add("-processorpath"); 976 opts.add(System.getProperty("test.class.path") + File.pathSeparator + apClasses.toString()); 977 opts.add("-processor"); 978 opts.add("AP"); 979 new JavacTask(tb) 980 .options(opts) 981 .outdir(classes) 982 .files(findJavaFiles(src)) 983 .run() 984 .writeAll(); 985 } 986 987 private void compileAP(Path target, String code) { 988 String processorCode = 989 "import java.util.*;\n" + 990 "import javax.annotation.processing.*;\n" + 991 "import javax.lang.model.*;\n" + 992 "import javax.lang.model.element.*;\n" + 993 "import javax.lang.model.type.*;\n" + 994 "import javax.lang.model.util.*;\n" + 995 "import javax.tools.*;\n" + 996 "@SupportedAnnotationTypes(\"*\")\n" + 997 "public final class AP extends AnnotationProcessing.GeneratingAP {\n" + 998 "\n" + 999 " int round;\n" + 1000 "\n" + 1001 " @Override\n" + 1002 " public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {\n" + 1003 " if (round++ != 0)\n" + 1004 " return false;\n" + 1005 " Filer filer = processingEnv.getFiler();\n" + 1006 " TypeElement jlObject = processingEnv.getElementUtils().getTypeElement(\"java.lang.Object\");\n" + 1007 code + ";\n" + 1008 " return false;\n" + 1009 " }\n" + 1010 " }\n"; 1011 new JavacTask(tb) 1012 .options("-classpath", System.getProperty("test.class.path")) 1013 .sources(processorCode) 1014 .outdir(target) 1015 .run() 1016 .writeAll(); 1017 } 1018 1019 @Test 1020 public void testGenerateInUnnamedModeAPI(Path base) throws Exception { 1021 Path classes = base.resolve("classes"); 1022 1023 Files.createDirectories(classes); 1024 1025 Path src = base.resolve("src"); 1026 1027 tb.writeJavaFiles(src, 1028 "class T {}"); 1029 1030 new JavacTask(tb) 1031 .options("-processor", UnnamedModeAPITestAP.class.getName(), 1032 "-sourcepath", src.toString()) 1033 .outdir(classes) 1034 .files(findJavaFiles(src)) 1035 .run() 1036 .writeAll(); 1037 1038 assertFileExists(classes, "Impl1.class"); 1039 assertFileExists(classes, "Impl2.class"); 1040 } 1041 1042 @Test 1043 public void testGenerateInNoModeAPI(Path base) throws Exception { 1044 Path classes = base.resolve("classes"); 1045 1046 Files.createDirectories(classes); 1047 1048 Path src = base.resolve("src"); 1049 1050 tb.writeJavaFiles(src, 1051 "class T {}"); 1052 1053 new JavacTask(tb) 1054 .options("-processor", UnnamedModeAPITestAP.class.getName(), 1055 "-source", "8", "-target", "8", 1056 "-sourcepath", src.toString()) 1057 .outdir(classes) 1058 .files(findJavaFiles(src)) 1059 .run() 1060 .writeAll(); 1061 1062 assertFileExists(classes, "Impl1.class"); 1063 assertFileExists(classes, "Impl2.class"); 1064 } 1065 1066 @SupportedAnnotationTypes("*") 1067 public static final class UnnamedModeAPITestAP extends GeneratingAP { 1068 1069 int round; 1070 1071 @Override 1072 public synchronized void init(ProcessingEnvironment processingEnv) { 1073 super.init(processingEnv); 1074 } 1075 1076 @Override 1077 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { 1078 if (round++ != 0) 1079 return false; 1080 1081 Filer filer = processingEnv.getFiler(); 1082 1083 //must not generate to unnamed package: 1084 createSource(() -> filer.createSourceFile("Impl1"), "Impl1", "class Impl1 {}"); 1085 createClass(() -> filer.createClassFile("Impl2"), "Impl2", "class Impl2 {}"); 1086 1087 return false; 1088 } 1089 1090 } 1091 1092 @Test 1093 public void testDisambiguateAnnotations(Path base) throws Exception { 1094 Path classes = base.resolve("classes"); 1095 1096 Files.createDirectories(classes); 1097 1098 Path src = base.resolve("src"); 1099 Path m1 = src.resolve("m1x"); 1100 1101 tb.writeJavaFiles(m1, 1102 "module m1x { exports api; }", 1103 "package api; public @interface A {}", 1104 "package api; public @interface B {}"); 1105 1106 Path m2 = src.resolve("m2x"); 1107 1108 tb.writeJavaFiles(m2, 1109 "module m2x { exports api; }", 1110 "package api; public @interface A {}", 1111 "package api; public @interface B {}"); 1112 1113 Path m3 = src.resolve("m3x"); 1114 1115 tb.writeJavaFiles(m3, 1116 "module m3x { requires m1x; }", 1117 "package impl; import api.*; @A @B public class T {}"); 1118 1119 Path m4 = src.resolve("m4x"); 1120 1121 tb.writeJavaFiles(m4, 1122 "module m4x { requires m2x; }", 1123 "package impl; import api.*; @A @B public class T {}"); 1124 1125 List<String> log; 1126 List<String> expected; 1127 1128 log = new JavacTask(tb) 1129 .options("-processor", SelectAnnotationATestAP.class.getName() + "," + SelectAnnotationBTestAP.class.getName(), 1130 "--module-source-path", src.toString(), 1131 "-m", "m1x,m2x") 1132 .outdir(classes) 1133 .run() 1134 .writeAll() 1135 .getOutputLines(OutputKind.STDERR); 1136 1137 expected = List.of(""); 1138 1139 if (!expected.equals(log)) { 1140 throw new AssertionError("Output does not match; output: " + log); 1141 } 1142 1143 log = new JavacTask(tb) 1144 .options("-processor", SelectAnnotationATestAP.class.getName() + "," + SelectAnnotationBTestAP.class.getName(), 1145 "--module-source-path", src.toString(), 1146 "-m", "m3x") 1147 .outdir(classes) 1148 .run() 1149 .writeAll() 1150 .getOutputLines(OutputKind.STDERR); 1151 1152 expected = List.of("SelectAnnotationBTestAP", 1153 "SelectAnnotationBTestAP"); 1154 1155 if (!expected.equals(log)) { 1156 throw new AssertionError("Output does not match; output: " + log); 1157 } 1158 1159 log = new JavacTask(tb) 1160 .options("-processor", SelectAnnotationATestAP.class.getName() + "," + 1161 SelectAnnotationBTestAP.class.getName() + "," + 1162 SelectAnnotationAStrictTestAP.class.getName(), 1163 "--module-source-path", src.toString(), 1164 "-m", "m4x") 1165 .outdir(classes) 1166 .run() 1167 .writeAll() 1168 .getOutputLines(OutputKind.STDERR); 1169 1170 expected = List.of("SelectAnnotationATestAP", 1171 "SelectAnnotationBTestAP", 1172 "SelectAnnotationAStrictTestAP", 1173 "SelectAnnotationATestAP", 1174 "SelectAnnotationBTestAP", 1175 "SelectAnnotationAStrictTestAP"); 1176 1177 if (!expected.equals(log)) { 1178 throw new AssertionError("Output does not match; output: " + log); 1179 } 1180 } 1181 1182 @Test 1183 public void testDisambiguateAnnotationsUnnamedModule(Path base) throws Exception { 1184 Path classes = base.resolve("classes"); 1185 1186 Files.createDirectories(classes); 1187 1188 Path src = base.resolve("src"); 1189 1190 tb.writeJavaFiles(src, 1191 "package api; public @interface A {}", 1192 "package api; public @interface B {}", 1193 "package impl; import api.*; @A @B public class T {}"); 1194 1195 List<String> log = new JavacTask(tb) 1196 .options("-processor", SelectAnnotationATestAP.class.getName() + "," + 1197 SelectAnnotationBTestAP.class.getName() + "," + 1198 SelectAnnotationAStrictTestAP.class.getName()) 1199 .outdir(classes) 1200 .files(findJavaFiles(src)) 1201 .run() 1202 .writeAll() 1203 .getOutputLines(OutputKind.STDERR); 1204 1205 List<String> expected = List.of("SelectAnnotationBTestAP", 1206 "SelectAnnotationBTestAP"); 1207 1208 if (!expected.equals(log)) { 1209 throw new AssertionError("Output does not match; output: " + log); 1210 } 1211 } 1212 1213 @Test 1214 public void testDisambiguateAnnotationsNoModules(Path base) throws Exception { 1215 Path classes = base.resolve("classes"); 1216 1217 Files.createDirectories(classes); 1218 1219 Path src = base.resolve("src"); 1220 1221 tb.writeJavaFiles(src, 1222 "package api; public @interface A {}", 1223 "package api; public @interface B {}", 1224 "package impl; import api.*; @A @B public class T {}"); 1225 1226 List<String> log = new JavacTask(tb) 1227 .options("-processor", SelectAnnotationATestAP.class.getName() + "," + 1228 SelectAnnotationBTestAP.class.getName() + "," + 1229 SelectAnnotationAStrictTestAP.class.getName(), 1230 "-source", "8", "-target", "8") 1231 .outdir(classes) 1232 .files(findJavaFiles(src)) 1233 .run() 1234 .writeAll() 1235 .getOutputLines(OutputKind.STDERR); 1236 1237 List<String> expected = List.of("SelectAnnotationATestAP", 1238 "SelectAnnotationBTestAP", 1239 "SelectAnnotationATestAP", 1240 "SelectAnnotationBTestAP"); 1241 1242 if (!expected.equals(log)) { 1243 throw new AssertionError("Output does not match; output: " + log); 1244 } 1245 } 1246 1247 @SupportedAnnotationTypes("m2x/api.A") 1248 public static final class SelectAnnotationATestAP extends AbstractProcessor { 1249 1250 @Override 1251 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { 1252 System.err.println("SelectAnnotationATestAP"); 1253 1254 return false; 1255 } 1256 1257 } 1258 1259 @SupportedAnnotationTypes("api.B") 1260 public static final class SelectAnnotationBTestAP extends AbstractProcessor { 1261 1262 @Override 1263 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { 1264 System.err.println("SelectAnnotationBTestAP"); 1265 1266 return false; 1267 } 1268 1269 } 1270 1271 public static final class SelectAnnotationAStrictTestAP extends AbstractProcessor { 1272 1273 @Override 1274 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { 1275 System.err.println("SelectAnnotationAStrictTestAP"); 1276 1277 return false; 1278 } 1279 1280 @Override 1281 public Set<String> getSupportedAnnotationTypes() { 1282 return Set.of("m2x/api.A"); 1283 } 1284 1285 } 1286 1287 private static void writeFile(String content, Path base, String... pathElements) { 1288 try { 1289 Path file = resolveFile(base, pathElements); 1290 1291 Files.createDirectories(file.getParent()); 1292 1293 try (Writer out = Files.newBufferedWriter(file)) { 1294 out.append(content); 1295 } 1296 } catch (IOException ex) { 1297 throw new UncheckedIOException(ex); 1298 } 1299 } 1300 1301 @Test 1302 public void testUnboundLookup(Path base) throws Exception { 1303 Path src = base.resolve("src"); 1304 1305 tb.writeJavaFiles(src, 1306 "package impl.conflict.src; public class Impl { }"); 1307 1308 Path moduleSrc = base.resolve("module-src"); 1309 Path m1 = moduleSrc.resolve("m1x"); 1310 Path m2 = moduleSrc.resolve("m2x"); 1311 1312 Path classes = base.resolve("classes"); 1313 Path cpClasses = base.resolve("cpClasses"); 1314 1315 Files.createDirectories(classes); 1316 Files.createDirectories(cpClasses); 1317 1318 tb.writeJavaFiles(m1, 1319 "module m1x { }", 1320 "package impl1; public class Impl { }", 1321 "package impl.conflict.module; class Impl { }", 1322 "package impl.conflict.clazz; public class pkg { public static class I { } }", 1323 "package impl.conflict.src; public class Impl { }", 1324 "package nested.pack.pack; public class Impl { }", 1325 "package unique.nested; public class Impl { }"); 1326 1327 tb.writeJavaFiles(m2, 1328 "module m2x { }", 1329 "package impl2; public class Impl { }", 1330 "package impl.conflict.module; class Impl { }", 1331 "package impl.conflict; public class clazz { public static class pkg { } }", 1332 "package nested.pack; public class Impl { }"); 1333 1334 //from source: 1335 String log = new JavacTask(tb) 1336 .options("--module-source-path", moduleSrc.toString(), 1337 "--source-path", src.toString(), 1338 "-processorpath", System.getProperty("test.class.path"), 1339 "-processor", UnboundLookup.class.getName(), 1340 "-XDrawDiagnostics") 1341 .outdir(classes) 1342 .files(findJavaFiles(moduleSrc)) 1343 .run() 1344 .writeAll() 1345 .getOutput(OutputKind.DIRECT); 1346 1347 String moduleImplConflictString = 1348 "- compiler.note.multiple.elements: getTypeElement, impl.conflict.module.Impl, m2x, m1x"; 1349 String srcConflictString = 1350 "- compiler.note.multiple.elements: getTypeElement, impl.conflict.src.Impl, m1x, unnamed module"; 1351 1352 if (!log.contains(moduleImplConflictString) || 1353 !log.contains(srcConflictString)) { 1354 throw new AssertionError("Expected output not found: " + log); 1355 } 1356 1357 if (log.split(Pattern.quote(moduleImplConflictString)).length > 2) { 1358 throw new AssertionError("Too many warnings in: " + log); 1359 } 1360 1361 new JavacTask(tb) 1362 .options("--source-path", src.toString()) 1363 .outdir(cpClasses) 1364 .files(findJavaFiles(src)) 1365 .run() 1366 .writeAll(); 1367 1368 //from classfiles: 1369 new JavacTask(tb) 1370 .options("--module-path", classes.toString(), 1371 "--class-path", cpClasses.toString(), 1372 "--add-modules", "m1x,m2x", 1373 "-processorpath", System.getProperty("test.class.path"), 1374 "-processor", UnboundLookup.class.getName(), 1375 "-proc:only") 1376 .classes("java.lang.Object") 1377 .run() 1378 .writeAll(); 1379 1380 //source 8: 1381 new JavacTask(tb) 1382 .options("--source-path", src.toString(), 1383 "-source", "8", 1384 "-processorpath", System.getProperty("test.class.path"), 1385 "-processor", UnboundLookup8.class.getName()) 1386 .outdir(cpClasses) 1387 .files(findJavaFiles(src)) 1388 .run() 1389 .writeAll(); 1390 1391 } 1392 1393 @SupportedAnnotationTypes("*") 1394 public static final class UnboundLookup extends AbstractProcessor { 1395 1396 @Override 1397 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { 1398 assertTypeElementExists("impl1.Impl", "m1x"); 1399 assertPackageElementExists("impl1", "m1x"); 1400 assertTypeElementExists("impl2.Impl", "m2x"); 1401 assertTypeElementExists("impl.conflict.clazz.pkg.I", "m1x"); 1402 assertTypeElementExists("impl.conflict.clazz", "m2x"); 1403 assertPackageElementExists("impl.conflict.clazz", "m1x"); 1404 assertPackageElementExists("impl2", "m2x"); 1405 assertPackageElementExists("nested.pack.pack", "m1x"); 1406 assertPackageElementExists("nested.pack", "m2x"); 1407 assertTypeElementExists("unique.nested.Impl", "m1x"); 1408 assertTypeElementNotFound("impl.conflict.module.Impl"); 1409 assertTypeElementNotFound("impl.conflict.module.Impl"); //check that the warning/note is produced only once 1410 assertPackageElementNotFound("impl.conflict.module"); 1411 assertTypeElementNotFound("impl.conflict.src.Impl"); 1412 assertPackageElementNotFound("impl.conflict.src"); 1413 assertTypeElementNotFound("impl.conflict.clazz.pkg"); 1414 assertPackageElementNotFound("unique"); //do not return packages without members in module mode 1415 assertTypeElementNotFound("nested"); //cannot distinguish between m1x and m2x 1416 1417 return false; 1418 } 1419 1420 private void assertTypeElementExists(String name, String expectedModule) { 1421 assertElementExists(name, "class", processingEnv.getElementUtils() :: getTypeElement, expectedModule); 1422 } 1423 1424 private void assertPackageElementExists(String name, String expectedModule) { 1425 assertElementExists(name, "package", processingEnv.getElementUtils() :: getPackageElement, expectedModule); 1426 } 1427 1428 private void assertElementExists(String name, String type, Function<String, Element> getter, String expectedModule) { 1429 Element clazz = getter.apply(name); 1430 1431 if (clazz == null) { 1432 throw new AssertionError("No " + name + " " + type + " found."); 1433 } 1434 1435 ModuleElement mod = processingEnv.getElementUtils().getModuleOf(clazz); 1436 1437 if (!mod.getQualifiedName().contentEquals(expectedModule)) { 1438 throw new AssertionError(name + " found in an unexpected module: " + mod.getQualifiedName()); 1439 } 1440 } 1441 1442 private void assertTypeElementNotFound(String name) { 1443 assertElementNotFound(name, processingEnv.getElementUtils() :: getTypeElement); 1444 } 1445 1446 private void assertPackageElementNotFound(String name) { 1447 assertElementNotFound(name, processingEnv.getElementUtils() :: getPackageElement); 1448 } 1449 1450 private void assertElementNotFound(String name, Function<String, Element> getter) { 1451 Element found = getter.apply(name); 1452 1453 if (found != null) { 1454 fail("Element found unexpectedly: " + found); 1455 } 1456 } 1457 1458 @Override 1459 public SourceVersion getSupportedSourceVersion() { 1460 return SourceVersion.latest(); 1461 } 1462 1463 } 1464 1465 @SupportedAnnotationTypes("*") 1466 public static final class UnboundLookup8 extends AbstractProcessor { 1467 1468 @Override 1469 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { 1470 if (processingEnv.getElementUtils().getTypeElement("impl.conflict.src.Impl") == null) { 1471 throw new AssertionError("impl.conflict.src.Impl."); 1472 } 1473 1474 if (processingEnv.getElementUtils().getModuleElement("java.base") != null) { 1475 throw new AssertionError("getModuleElement != null for -source 8"); 1476 } 1477 1478 return false; 1479 } 1480 1481 @Override 1482 public SourceVersion getSupportedSourceVersion() { 1483 return SourceVersion.latest(); 1484 } 1485 1486 } 1487 1488 @Test 1489 public void testWrongDefaultTargetModule(Path base) throws Exception { 1490 Path src = base.resolve("src"); 1491 1492 tb.writeJavaFiles(src, 1493 "package test; public class Test { }"); 1494 1495 Path classes = base.resolve("classes"); 1496 1497 Files.createDirectories(classes); 1498 1499 List<String> log = new JavacTask(tb) 1500 .options("--default-module-for-created-files=m!", 1501 "-XDrawDiagnostics") 1502 .outdir(classes) 1503 .files(findJavaFiles(src)) 1504 .run(Task.Expect.FAIL) 1505 .writeAll() 1506 .getOutputLines(OutputKind.DIRECT); 1507 1508 List<String> expected = Arrays.asList( 1509 "- compiler.err.bad.name.for.option: --default-module-for-created-files, m!" 1510 ); 1511 1512 if (!log.equals(expected)) { 1513 throw new AssertionError("Expected output not found."); 1514 } 1515 } 1516 1517 private static void assertNonNull(String msg, Object val) { 1518 if (val == null) { 1519 throw new AssertionError(msg); 1520 } 1521 } 1522 1523 private static void assertNull(String msg, Object val) { 1524 if (val != null) { 1525 throw new AssertionError(msg); 1526 } 1527 } 1528 1529 private static void assertEquals(Object expected, Object actual) { 1530 if (!Objects.equals(expected, actual)) { 1531 throw new AssertionError("expected: " + expected + "; actual=" + actual); 1532 } 1533 } 1534 1535 private static void assertFileExists(Path base, String... pathElements) { 1536 Path file = resolveFile(base, pathElements); 1537 1538 if (!Files.exists(file)) { 1539 throw new AssertionError("Expected file: " + file + " exist, but it does not."); 1540 } 1541 } 1542 1543 private static void assertFileNotExists(Path base, String... pathElements) { 1544 Path file = resolveFile(base, pathElements); 1545 1546 if (Files.exists(file)) { 1547 throw new AssertionError("Expected file: " + file + " exist, but it does not."); 1548 } 1549 } 1550 1551 static Path resolveFile(Path base, String... pathElements) { 1552 Path file = base; 1553 1554 for (String el : pathElements) { 1555 file = file.resolve(el); 1556 } 1557 1558 return file; 1559 } 1560 1561 private static void fail(String msg) { 1562 throw new AssertionError(msg); 1563 } 1564 1565} 1566