1 /*
   2  * Copyright (c) 2012, 2019, 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 8171355
  27  * @summary Test behavior of javax.lang.model.util.Elements.getOrigin.
  28  * @library /tools/lib
  29  * @modules jdk.compiler/com.sun.tools.javac.api
  30  *          jdk.compiler/com.sun.tools.javac.main
  31  *          jdk.jdeps/com.sun.tools.classfile
  32  * @build toolbox.ToolBox toolbox.JavacTask toolbox.TestRunner
  33  * @build TestOrigin ListMembersAP
  34  * @run main TestOrigin
  35  */
  36 
  37 import java.io.OutputStream;
  38 import java.nio.file.Files;
  39 import java.nio.file.Path;
  40 import java.nio.file.Paths;
  41 import java.util.Arrays;
  42 import java.util.HashMap;
  43 import java.util.List;
  44 import java.util.Map;
  45 import java.util.Set;
  46 
  47 import javax.annotation.processing.*;
  48 import javax.lang.model.SourceVersion;
  49 import javax.lang.model.element.*;
  50 import javax.lang.model.element.ModuleElement.Directive;
  51 import javax.lang.model.element.ModuleElement.ExportsDirective;
  52 import javax.lang.model.element.ModuleElement.OpensDirective;
  53 import javax.lang.model.element.ModuleElement.RequiresDirective;
  54 import javax.lang.model.util.Elements;
  55 
  56 import com.sun.tools.classfile.Attribute;
  57 import com.sun.tools.classfile.Attributes;
  58 import com.sun.tools.classfile.ClassFile;
  59 import com.sun.tools.classfile.ClassWriter;
  60 import com.sun.tools.classfile.Module_attribute;
  61 import com.sun.tools.classfile.Module_attribute.ExportsEntry;
  62 import com.sun.tools.classfile.Module_attribute.OpensEntry;
  63 import com.sun.tools.classfile.Module_attribute.RequiresEntry;
  64 import toolbox.JavacTask;
  65 import toolbox.Task;
  66 import toolbox.TestRunner;
  67 import toolbox.ToolBox;
  68 
  69 public class TestOrigin extends TestRunner {
  70 
  71     private final ToolBox tb;
  72 
  73     TestOrigin() {
  74         super(System.err);
  75         tb = new ToolBox();
  76     }
  77 
  78     public static void main(String... args) throws Exception {
  79         new TestOrigin().runTests(m -> new Object[] { Paths.get(m.getName()) });
  80     }
  81 
  82     @Test
  83     public void testGeneratedConstr(Path base) throws Exception {
  84         Path src = base.resolve("src");
  85         tb.writeJavaFiles(src,
  86                           "package test; public class Test { private void test() { } }",
  87                           "class Dummy {}");
  88         Path classes = base.resolve("classes");
  89         tb.createDirectories(classes);
  90 
  91         List<String> log;
  92         List<String> expected;
  93 
  94         //from source:
  95         log = new JavacTask(tb)
  96             .options("-processor", "ListMembersAP")
  97             .outdir(classes)
  98             .files(tb.findJavaFiles(src))
  99             .run()
 100             .writeAll()
 101             .getOutputLines(Task.OutputKind.STDOUT);
 102 
 103         expected = Arrays.asList(
 104                 "<init>:MANDATED",
 105                 "test:EXPLICIT");
 106 
 107         if (!expected.equals(log))
 108             throw new AssertionError("expected output not found: " + log);
 109 
 110         //from class:
 111         log = new JavacTask(tb)
 112             .options("-classpath", classes.toString(),
 113                      "-processorpath", System.getProperty("test.classes"),
 114                      "-processor", "ListMembersAP")
 115             .outdir(classes)
 116             .files(src.resolve("Dummy.java"))
 117             .run()
 118             .writeAll()
 119             .getOutputLines(Task.OutputKind.STDOUT);
 120 
 121         expected = Arrays.asList(
 122                 "<init>:EXPLICIT",
 123                 "test:EXPLICIT");
 124 
 125         if (!expected.equals(log))
 126             throw new AssertionError("expected output not found: " + log);
 127     }
 128 
 129     @Test
 130     public void testRepeatableAnnotations(Path base) throws Exception {
 131         Path src = base.resolve("src");
 132         tb.writeJavaFiles(src,
 133                           "package test; @A @A public class Test { }",
 134                           "package test;" +
 135                           "import java.lang.annotation.*;" +
 136                           "@Repeatable(Container.class)" +
 137                           "@Retention(RetentionPolicy.CLASS)" +
 138                           "@interface A {}",
 139                           "package test; @interface Container { A[] value(); }",
 140                           "class Dummy {}");
 141         Path classes = base.resolve("classes");
 142         tb.createDirectories(classes);
 143 
 144         List<String> log;
 145         List<String> expected;
 146 
 147         //from source:
 148         log = new JavacTask(tb)
 149             .options("-processor", ListAnnotationsAP.class.getName())
 150             .outdir(classes)
 151             .files(tb.findJavaFiles(src))
 152             .run()
 153             .writeAll()
 154             .getOutputLines(Task.OutputKind.STDOUT);
 155 
 156         expected = Arrays.asList("test.Container:MANDATED");
 157 
 158         if (!expected.equals(log))
 159             throw new AssertionError("expected output not found: " + log);
 160 
 161         //from class:
 162         log = new JavacTask(tb)
 163             .options("-classpath", classes.toString(),
 164                      "-processorpath", System.getProperty("test.classes"),
 165                      "-processor", ListAnnotationsAP.class.getName())
 166             .outdir(classes)
 167             .files(src.resolve("Dummy.java"))
 168             .run()
 169             .writeAll()
 170             .getOutputLines(Task.OutputKind.STDOUT);
 171 
 172         expected = Arrays.asList("test.Container:EXPLICIT");
 173 
 174         if (!expected.equals(log))
 175             throw new AssertionError("expected output not found: " + log);
 176     }
 177 
 178     @SupportedAnnotationTypes("*")
 179     public static final class ListAnnotationsAP extends AbstractProcessor {
 180 
 181         @Override
 182         public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
 183             if (!roundEnv.processingOver())
 184                 return false;
 185 
 186             Elements elements = processingEnv.getElementUtils();
 187             TypeElement test = elements.getTypeElement("test.Test");
 188 
 189             for (AnnotationMirror am : test.getAnnotationMirrors()) {
 190                 System.out.println(am.getAnnotationType() + ":" + elements.getOrigin(test, am));
 191             }
 192 
 193             return false;
 194         }
 195 
 196         @Override
 197         public SourceVersion getSupportedSourceVersion() {
 198             return SourceVersion.latestSupported();
 199         }
 200 
 201     }
 202 
 203     @Test
 204     public void testModuleDirectives(Path base) throws Exception {
 205         Path src = base.resolve("src");
 206         tb.writeJavaFiles(src,
 207                           "module m {}",
 208                           "package p1; class Test {}",
 209                           "package p2; class Test {}",
 210                           "package p3; class Test {}",
 211                           "package test; class Dummy {}");
 212         Path classes = base.resolve("classes");
 213         tb.createDirectories(classes);
 214 
 215         List<String> log;
 216         List<String> expected;
 217 
 218         //from source:
 219         log = new JavacTask(tb)
 220             .options("-processor", ListModuleAP.class.getName())
 221             .outdir(classes)
 222             .files(tb.findJavaFiles(src))
 223             .run()
 224             .writeAll()
 225             .getOutputLines(Task.OutputKind.STDOUT);
 226 
 227         expected = Arrays.asList("REQUIRES:java.base:MANDATED");
 228 
 229         if (!expected.equals(log))
 230             throw new AssertionError("expected output not found: " + log);
 231 
 232         tb.writeJavaFiles(src,
 233                           "module m {" +
 234                           "    requires java.base;" +
 235                           "    requires java.compiler;" +
 236                           "    requires jdk.compiler;" +
 237                           "    exports p1;" +
 238                           "    exports p2;" +
 239                           "    exports p3;" +
 240                           "    opens p1;" +
 241                           "    opens p2;" +
 242                           "    opens p3;" +
 243                           "}");
 244 
 245         new JavacTask(tb)
 246             .outdir(classes)
 247             .files(src.resolve("module-info.java"))
 248             .run()
 249             .writeAll();
 250 
 251         Path moduleInfo = classes.resolve("module-info.class");
 252         ClassFile cf = ClassFile.read(moduleInfo);
 253         Module_attribute module = (Module_attribute) cf.getAttribute(Attribute.Module);
 254 
 255         RequiresEntry[] newRequires = new RequiresEntry[3];
 256         newRequires[0] = new RequiresEntry(module.requires[0].requires_index,
 257                                            Module_attribute.ACC_MANDATED,
 258                                            module.requires[0].requires_version_index);
 259         newRequires[1] = new RequiresEntry(module.requires[1].requires_index,
 260                                            Module_attribute.ACC_SYNTHETIC,
 261                                            module.requires[1].requires_version_index);
 262         newRequires[2] = module.requires[2];
 263 
 264         ExportsEntry[] newExports = new ExportsEntry[3];
 265         newExports[0] = new ExportsEntry(module.exports[0].exports_index,
 266                                          Module_attribute.ACC_MANDATED,
 267                                          module.exports[0].exports_to_index);
 268         newExports[1] = new ExportsEntry(module.exports[1].exports_index,
 269                                          Module_attribute.ACC_SYNTHETIC,
 270                                          module.exports[1].exports_to_index);
 271         newExports[2] = module.exports[2];
 272 
 273         OpensEntry[] newOpens = new OpensEntry[3];
 274         newOpens[0] = new OpensEntry(module.opens[0].opens_index,
 275                                      Module_attribute.ACC_MANDATED,
 276                                      module.opens[0].opens_to_index);
 277         newOpens[1] = new OpensEntry(module.opens[1].opens_index,
 278                                      Module_attribute.ACC_SYNTHETIC,
 279                                      module.opens[1].opens_to_index);
 280         newOpens[2] = module.opens[2];
 281 
 282         Module_attribute newModule = new Module_attribute(module.attribute_name_index,
 283                                                           module.module_name,
 284                                                           module.module_flags,
 285                                                           module.module_version_index,
 286                                                           newRequires,
 287                                                           newExports,
 288                                                           newOpens,
 289                                                           module.uses_index,
 290                                                           module.provides);
 291         Map<String, Attribute> newAttributesMap = new HashMap<>(cf.attributes.map);
 292 
 293         newAttributesMap.put(Attribute.Module, newModule);
 294 
 295         Attributes newAttributes = new Attributes(newAttributesMap);
 296         ClassFile newClassFile = new ClassFile(cf.magic,
 297                                                cf.minor_version,
 298                                                cf.major_version,
 299                                                cf.constant_pool,
 300                                                cf.access_flags,
 301                                                cf.this_class,
 302                                                cf.super_class,
 303                                                cf.interfaces,
 304                                                cf.fields,
 305                                                cf.methods,
 306                                                newAttributes);
 307 
 308         try (OutputStream out = Files.newOutputStream(moduleInfo)) {
 309             new ClassWriter().write(newClassFile, out);
 310         }
 311 
 312         //from class:
 313         log = new JavacTask(tb)
 314             .options("-processor", ListModuleAP.class.getName())
 315             .outdir(classes)
 316             .files(src.resolve("test").resolve("Dummy.java"))
 317             .run()
 318             .writeAll()
 319             .getOutputLines(Task.OutputKind.STDOUT);
 320 
 321         expected = Arrays.asList(
 322                 "REQUIRES:java.base:MANDATED",
 323                 "REQUIRES:java.compiler:SYNTHETIC",
 324                 "REQUIRES:jdk.compiler:EXPLICIT",
 325                 "EXPORTS:p1:MANDATED",
 326                 "EXPORTS:p2:SYNTHETIC",
 327                 "EXPORTS:p3:EXPLICIT",
 328                 "OPENS:p1:MANDATED",
 329                 "OPENS:p2:SYNTHETIC",
 330                 "OPENS:p3:EXPLICIT");
 331 
 332         if (!expected.equals(log))
 333             throw new AssertionError("expected output not found: " + log);
 334     }
 335 
 336     @SupportedAnnotationTypes("*")
 337     public static final class ListModuleAP extends AbstractProcessor {
 338 
 339         @Override
 340         public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
 341             if (!roundEnv.processingOver())
 342                 return false;
 343 
 344             Elements elements = processingEnv.getElementUtils();
 345             ModuleElement m = elements.getModuleElement("m");
 346 
 347             for (Directive d : m.getDirectives()) {
 348                 switch (d.getKind()) {
 349                     case REQUIRES:
 350                         RequiresDirective rd = (RequiresDirective) d;
 351                         System.out.println(rd.getKind() + ":" +
 352                                            rd.getDependency().getQualifiedName() + ":" +
 353                                            elements.getOrigin(m, rd));
 354                         break;
 355                     case EXPORTS:
 356                         ExportsDirective ed = (ExportsDirective) d;
 357                         System.out.println(ed.getKind() + ":" +
 358                                            ed.getPackage() + ":" +
 359                                            elements.getOrigin(m, ed));
 360                         break;
 361                     case OPENS:
 362                         OpensDirective od = (OpensDirective) d;
 363                         System.out.println(od.getKind() + ":" +
 364                                            od.getPackage() + ":" +
 365                                            elements.getOrigin(m, od));
 366                         break;
 367                 }
 368             }
 369 
 370             return false;
 371         }
 372 
 373         @Override
 374         public SourceVersion getSupportedSourceVersion() {
 375             return SourceVersion.latestSupported();
 376         }
 377 
 378     }
 379 
 380 }