AnnotationsOnModules.java revision 3822:d8766c39123a
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
35import java.io.File;
36import java.nio.file.Files;
37import java.nio.file.Path;
38import java.util.Arrays;
39import java.util.HashSet;
40import java.util.List;
41import java.util.Objects;
42import java.util.Set;
43import java.util.stream.Collectors;
44
45import javax.annotation.processing.AbstractProcessor;
46import javax.annotation.processing.RoundEnvironment;
47import javax.annotation.processing.SupportedAnnotationTypes;
48import javax.annotation.processing.SupportedOptions;
49import javax.lang.model.element.AnnotationMirror;
50import javax.lang.model.element.ModuleElement;
51import javax.lang.model.element.TypeElement;
52
53import com.sun.tools.classfile.Attribute;
54import com.sun.tools.classfile.ClassFile;
55import com.sun.tools.classfile.RuntimeInvisibleAnnotations_attribute;
56import com.sun.tools.classfile.RuntimeVisibleAnnotations_attribute;
57import toolbox.JavacTask;
58import toolbox.Task.OutputKind;
59
60public 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}
406