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