1 /* 2 * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /** 25 * @test 26 * @summary Test --add-modules and --limit-modules; also test the "enabled" modules. 27 * @library /tools/lib 28 * @modules jdk.compiler/com.sun.tools.javac.api 29 * jdk.compiler/com.sun.tools.javac.main 30 * jdk.jdeps/com.sun.tools.classfile 31 * @build toolbox.ToolBox toolbox.JarTask toolbox.JavacTask toolbox.JavaTask ModuleTestBase 32 * @run main AnnotationsOnModules 33 */ 34 35 import java.io.File; 36 import java.nio.file.Files; 37 import java.nio.file.Path; 38 import java.util.Arrays; 39 import java.util.HashSet; 40 import java.util.List; 41 import java.util.Objects; 42 import java.util.Set; 43 import java.util.stream.Collectors; 44 45 import javax.annotation.processing.AbstractProcessor; 46 import javax.annotation.processing.RoundEnvironment; 47 import javax.annotation.processing.SupportedAnnotationTypes; 48 import javax.annotation.processing.SupportedOptions; 49 import javax.lang.model.element.AnnotationMirror; 50 import javax.lang.model.element.ModuleElement; 51 import javax.lang.model.element.TypeElement; 52 53 import com.sun.tools.classfile.Attribute; 54 import com.sun.tools.classfile.ClassFile; 55 import com.sun.tools.classfile.RuntimeInvisibleAnnotations_attribute; 56 import com.sun.tools.classfile.RuntimeVisibleAnnotations_attribute; 57 import toolbox.JavacTask; 58 import toolbox.Task.OutputKind; 59 60 public class AnnotationsOnModules extends ModuleTestBase { 61 62 public static void main(String... args) throws Exception { 63 AnnotationsOnModules t = new AnnotationsOnModules(); 64 t.runTests(); 65 } 66 67 @Test 68 public void testSimpleAnnotation(Path base) throws Exception { 69 Path moduleSrc = base.resolve("module-src"); 70 Path m1 = moduleSrc.resolve("m1x"); 71 72 tb.writeJavaFiles(m1, 73 "@Deprecated module m1x { }"); 74 75 Path modulePath = base.resolve("module-path"); 76 77 Files.createDirectories(modulePath); 78 79 new JavacTask(tb) 80 .options("--module-source-path", moduleSrc.toString()) 81 .outdir(modulePath) 82 .files(findJavaFiles(m1)) 83 .run() 84 .writeAll(); 85 86 ClassFile cf = ClassFile.read(modulePath.resolve("m1x").resolve("module-info.class")); 87 RuntimeVisibleAnnotations_attribute annotations = (RuntimeVisibleAnnotations_attribute) cf.attributes.map.get(Attribute.RuntimeVisibleAnnotations); 88 89 if (annotations == null || annotations.annotations.length != 1) { 90 throw new AssertionError("Annotations not correct!"); 91 } 92 } 93 94 @Test 95 public void testAnnotationWithImport(Path base) throws Exception { 96 Path moduleSrc = base.resolve("module-src"); 97 Path m1 = moduleSrc.resolve("m1x"); 98 99 tb.writeJavaFiles(m1, 100 "import m1x.A; @A module m1x { }", 101 "package m1x; import java.lang.annotation.*; @Target(ElementType.MODULE) public @interface A {}"); 102 103 Path modulePath = base.resolve("module-path"); 104 105 Files.createDirectories(modulePath); 106 107 new JavacTask(tb) 108 .options("--module-source-path", moduleSrc.toString()) 109 .outdir(modulePath) 110 .files(findJavaFiles(m1)) 111 .run() 112 .writeAll(); 113 114 ClassFile cf = ClassFile.read(modulePath.resolve("m1x").resolve("module-info.class")); 115 RuntimeInvisibleAnnotations_attribute annotations = (RuntimeInvisibleAnnotations_attribute) cf.attributes.map.get(Attribute.RuntimeInvisibleAnnotations); 116 117 if (annotations == null || annotations.annotations.length != 1) { 118 throw new AssertionError("Annotations not correct!"); 119 } 120 } 121 122 @Test 123 public void testModuleInfoAnnotationsInAPI(Path base) throws Exception { 124 Path moduleSrc = base.resolve("module-src"); 125 Path m1 = moduleSrc.resolve("m1x"); 126 127 tb.writeJavaFiles(m1, 128 "import m1x.*; @A @Deprecated @E @E module m1x { }", 129 "package m1x; import java.lang.annotation.*; @Target(ElementType.MODULE) public @interface A {}", 130 "package m1x; import java.lang.annotation.*; @Target(ElementType.MODULE) @Repeatable(C.class) public @interface E {}", 131 "package m1x; import java.lang.annotation.*; @Target(ElementType.MODULE) public @interface C { public E[] value(); }"); 132 133 Path modulePath = base.resolve("module-path"); 134 135 Files.createDirectories(modulePath); 136 137 new JavacTask(tb) 138 .options("--module-source-path", moduleSrc.toString(), 139 "-processor", AP.class.getName()) 140 .outdir(modulePath) 141 .files(findJavaFiles(m1)) 142 .run() 143 .writeAll(); 144 145 Path src = base.resolve("src"); 146 147 tb.writeJavaFiles(src, 148 "class T {}"); 149 150 Path out = base.resolve("out"); 151 152 Files.createDirectories(out); 153 154 new JavacTask(tb) 155 .options("--module-path", modulePath.toString(), 156 "--add-modules", "m1x", 157 "-processor", AP.class.getName()) 158 .outdir(out) 159 .files(findJavaFiles(src)) 160 .run() 161 .writeAll(); 162 163 new JavacTask(tb) 164 .options("--module-path", modulePath.toString() + File.pathSeparator + out.toString(), 165 "--add-modules", "m1x", 166 "-processor", AP.class.getName(), 167 "-proc:only") 168 .classes("m1x/m1x.A") 169 .files(findJavaFiles(src)) 170 .run() 171 .writeAll(); 172 } 173 174 @SupportedAnnotationTypes("*") 175 public static final class AP extends AbstractProcessor { 176 177 @Override 178 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { 179 ModuleElement m1 = processingEnv.getElementUtils().getModuleElement("m1x"); 180 Set<String> actualAnnotations = new HashSet<>(); 181 Set<String> expectedAnnotations = 182 new HashSet<>(Arrays.asList("@m1x.A", "@java.lang.Deprecated", "@m1x.C({@m1x.E, @m1x.E})")); 183 184 for (AnnotationMirror am : m1.getAnnotationMirrors()) { 185 actualAnnotations.add(am.toString()); 186 } 187 188 if (!expectedAnnotations.equals(actualAnnotations)) { 189 throw new AssertionError("Incorrect annotations: " + actualAnnotations); 190 } 191 192 return false; 193 } 194 195 } 196 197 @Test 198 public void testModuleDeprecation(Path base) throws Exception { 199 Path moduleSrc = base.resolve("module-src"); 200 Path m1 = moduleSrc.resolve("m1x"); 201 202 tb.writeJavaFiles(m1, 203 "@Deprecated module m1x { }"); 204 205 Path m2 = moduleSrc.resolve("m2x"); 206 207 tb.writeJavaFiles(m2, 208 "@Deprecated module m2x { }"); 209 210 Path m3 = moduleSrc.resolve("m3x"); 211 212 Path modulePath = base.resolve("module-path"); 213 214 Files.createDirectories(modulePath); 215 216 List<String> actual; 217 List<String> expected; 218 219 for (String suppress : new String[] {"", "@Deprecated ", "@SuppressWarnings(\"deprecation\") "}) { 220 tb.writeJavaFiles(m3, 221 suppress + "module m3x {\n" + 222 " requires m1x;\n" + 223 " exports api to m1x, m2x;\n" + 224 "}", 225 "package api; public class Api { }"); 226 System.err.println("compile m3x"); 227 actual = new JavacTask(tb) 228 .options("--module-source-path", moduleSrc.toString(), 229 "-XDrawDiagnostics") 230 .outdir(modulePath) 231 .files(findJavaFiles(moduleSrc)) 232 .run() 233 .writeAll() 234 .getOutputLines(OutputKind.DIRECT); 235 236 if (suppress.isEmpty()) { 237 expected = Arrays.asList( 238 "- compiler.note.deprecated.filename: module-info.java", 239 "- compiler.note.deprecated.recompile"); 240 } else { 241 expected = Arrays.asList(""); 242 } 243 244 if (!expected.equals(actual)) { 245 throw new AssertionError("Unexpected output: " + actual + "; suppress: " + suppress); 246 } 247 248 System.err.println("compile m3x with -Xlint:-deprecation"); 249 actual = new JavacTask(tb) 250 .options("--module-source-path", moduleSrc.toString(), 251 "-XDrawDiagnostics", 252 "-Xlint:deprecation") 253 .outdir(modulePath) 254 .files(findJavaFiles(moduleSrc)) 255 .run() 256 .writeAll() 257 .getOutputLines(OutputKind.DIRECT); 258 259 if (suppress.isEmpty()) { 260 expected = Arrays.asList( 261 "module-info.java:2:14: compiler.warn.has.been.deprecated.module: m1x", 262 "1 warning"); 263 } else { 264 expected = Arrays.asList(""); 265 } 266 267 if (!expected.equals(actual)) { 268 throw new AssertionError("Unexpected output: " + actual + "; suppress: " + suppress); 269 } 270 271 //load the deprecated module-infos from classfile: 272 System.err.println("compile m3x with -Xlint:-deprecation, loading deprecated modules from classes"); 273 actual = new JavacTask(tb) 274 .options("--module-path", modulePath.toString(), 275 "-XDrawDiagnostics", 276 "-Xlint:deprecation") 277 .outdir(modulePath.resolve("m3x")) 278 .files(findJavaFiles(moduleSrc.resolve("m3x"))) 279 .run() 280 .writeAll() 281 .getOutputLines(OutputKind.DIRECT); 282 283 if (!expected.equals(actual)) { 284 throw new AssertionError("Unexpected output: " + actual + "; suppress: " + suppress); 285 } 286 } 287 } 288 289 @Test 290 public void testAttributeValues(Path base) throws Exception { 291 class TestCase { 292 public final String extraDecl; 293 public final String decl; 294 public final String use; 295 public final String expectedAnnotations; 296 297 public TestCase(String extraDecl, String decl, String use, String expectedAnnotations) { 298 this.extraDecl = extraDecl; 299 this.decl = decl; 300 this.use = use; 301 this.expectedAnnotations = expectedAnnotations; 302 } 303 } 304 305 TestCase[] testCases = new TestCase[] { 306 new TestCase("package test; public enum E {A, B;}", 307 "public E value();", 308 "test.E.A", 309 "@test.A(test.E.A)"), 310 new TestCase("package test; public enum E {A, B;}", 311 "public E[] value();", 312 "{test.E.A, test.E.B}", 313 "@test.A({test.E.A, test.E.B})"), 314 new TestCase("package test; public class Extra {}", 315 "public Class value();", 316 "test.Extra.class", 317 "@test.A(test.Extra.class)"), 318 new TestCase("package test; public class Extra {}", 319 "public Class[] value();", 320 "{test.Extra.class, String.class}", 321 "@test.A({test.Extra.class, java.lang.String.class})"), 322 new TestCase("package test; public @interface Extra { public Class value(); }", 323 "public test.Extra value();", 324 "@test.Extra(String.class)", 325 "@test.A(@test.Extra(java.lang.String.class))"), 326 new TestCase("package test; public @interface Extra { public Class value(); }", 327 "public test.Extra[] value();", 328 "{@test.Extra(String.class), @test.Extra(Integer.class)}", 329 "@test.A({@test.Extra(java.lang.String.class), @test.Extra(java.lang.Integer.class)})"), 330 new TestCase("package test; public class Any { }", 331 "public int value();", 332 "1", 333 "@test.A(1)"), 334 new TestCase("package test; public class Any { }", 335 "public int[] value();", 336 "{1, 2}", 337 "@test.A({1, 2})"), 338 }; 339 340 Path extraSrc = base.resolve("extra-src"); 341 tb.writeJavaFiles(extraSrc, 342 "class Any {}"); 343 344 int count = 0; 345 346 for (TestCase tc : testCases) { 347 Path testBase = base.resolve(String.valueOf(count)); 348 Path moduleSrc = testBase.resolve("module-src"); 349 Path m = moduleSrc.resolve("m"); 350 351 tb.writeJavaFiles(m, 352 "@test.A(" + tc.use + ") module m { }", 353 "package test; @java.lang.annotation.Target(java.lang.annotation.ElementType.MODULE) public @interface A { " + tc.decl + "}", 354 tc.extraDecl); 355 356 Path modulePath = testBase.resolve("module-path"); 357 358 Files.createDirectories(modulePath); 359 360 new JavacTask(tb) 361 .options("--module-source-path", moduleSrc.toString()) 362 .outdir(modulePath) 363 .files(findJavaFiles(moduleSrc)) 364 .run() 365 .writeAll(); 366 367 Path classes = testBase.resolve("classes"); 368 369 Files.createDirectories(classes); 370 371 new JavacTask(tb) 372 .options("--module-path", modulePath.toString(), 373 "--add-modules", "m", 374 "-processorpath", System.getProperty("test.classes"), 375 "-processor", ProxyTypeValidator.class.getName(), 376 "-A" + OPT_EXPECTED_ANNOTATIONS + "=" + tc.expectedAnnotations) 377 .outdir(classes) 378 .files(findJavaFiles(extraSrc)) 379 .run() 380 .writeAll(); 381 } 382 } 383 384 private static final String OPT_EXPECTED_ANNOTATIONS = "expectedAnnotations"; 385 386 @SupportedAnnotationTypes("*") 387 @SupportedOptions(OPT_EXPECTED_ANNOTATIONS) 388 public static final class ProxyTypeValidator extends AbstractProcessor { 389 390 @Override 391 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { 392 ModuleElement m = processingEnv.getElementUtils().getModuleElement("m"); 393 String actualTypes = m.getAnnotationMirrors() 394 .stream() 395 .map(am -> am.toString()) 396 .collect(Collectors.joining(", ")); 397 if (!Objects.equals(actualTypes, processingEnv.getOptions().get(OPT_EXPECTED_ANNOTATIONS))) { 398 throw new IllegalStateException("Expected annotations not found, actual: " + actualTypes); 399 } 400 return false; 401 } 402 403 } 404 405 }