langtools/test/tools/javac/importscope/dependencies/DependenciesTest.java
changeset 27857 7e913a535736
child 30730 d3ce7619db2c
equal deleted inserted replaced
27856:d4711a6931e2 27857:7e913a535736
       
     1 /*
       
     2  * Copyright (c) 2014, 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 7101822
       
    27  * @summary Verify that the processing of classes in TypeEnter runs in the correct order.
       
    28  * @library /tools/lib
       
    29  * @build annotations.TriggersComplete annotations.TriggersCompleteRepeat annotations.Phase
       
    30  * @build DependenciesTest
       
    31  * @run main DependenciesTest
       
    32  */
       
    33 
       
    34 import java.io.IOException;
       
    35 import java.net.URI;
       
    36 import java.nio.file.Files;
       
    37 import java.nio.file.Path;
       
    38 import java.nio.file.Paths;
       
    39 import java.util.ArrayList;
       
    40 import java.util.Arrays;
       
    41 import java.util.Collection;
       
    42 import java.util.Collections;
       
    43 import java.util.HashMap;
       
    44 import java.util.HashSet;
       
    45 import java.util.List;
       
    46 import java.util.Map;
       
    47 import java.util.Map.Entry;
       
    48 import java.util.Objects;
       
    49 import java.util.Set;
       
    50 import java.util.Stack;
       
    51 import java.util.stream.Stream;
       
    52 
       
    53 import javax.lang.model.element.AnnotationMirror;
       
    54 import javax.lang.model.element.AnnotationValue;
       
    55 import javax.lang.model.element.Element;
       
    56 import javax.lang.model.element.ExecutableElement;
       
    57 import javax.lang.model.element.Name;
       
    58 import javax.lang.model.element.TypeElement;
       
    59 import javax.lang.model.type.DeclaredType;
       
    60 import javax.lang.model.type.TypeMirror;
       
    61 import javax.lang.model.util.Elements;
       
    62 import javax.tools.JavaFileObject;
       
    63 import javax.tools.SimpleJavaFileObject;
       
    64 
       
    65 import annotations.*;
       
    66 import com.sun.source.tree.AnnotationTree;
       
    67 
       
    68 import com.sun.source.tree.ClassTree;
       
    69 import com.sun.source.tree.CompilationUnitTree;
       
    70 import com.sun.source.tree.ImportTree;
       
    71 import com.sun.source.tree.Tree;
       
    72 import com.sun.source.util.JavacTask;
       
    73 import com.sun.source.util.SourcePositions;
       
    74 import com.sun.source.util.TreePathScanner;
       
    75 import com.sun.source.util.Trees;
       
    76 import com.sun.tools.javac.api.JavacTool;
       
    77 import com.sun.tools.javac.api.JavacTrees;
       
    78 import com.sun.tools.javac.code.Symbol.ClassSymbol;
       
    79 import com.sun.tools.javac.file.JavacFileManager;
       
    80 import com.sun.tools.javac.tree.JCTree;
       
    81 import com.sun.tools.javac.util.Context;
       
    82 import com.sun.tools.javac.util.Context.Factory;
       
    83 import com.sun.tools.javac.util.Dependencies;
       
    84 
       
    85 
       
    86 public class DependenciesTest {
       
    87     public static void main(String... args) throws IOException {
       
    88         new DependenciesTest().run();
       
    89     }
       
    90 
       
    91     void run() throws IOException {
       
    92         Path src = Paths.get(System.getProperty("test.src"), "tests");
       
    93 
       
    94         try (Stream<Path> tests = Files.list(src)) {
       
    95             tests.map(p -> Files.isRegularFile(p) ? Stream.of(p) : silentWalk(p))
       
    96                  .forEach(this :: runTest);
       
    97         }
       
    98     }
       
    99 
       
   100     Stream<Path> silentWalk(Path src) {
       
   101         try {
       
   102             return Files.walk(src).filter(Files :: isRegularFile);
       
   103         } catch (IOException ex) {
       
   104             throw new IllegalStateException(ex);
       
   105         }
       
   106     }
       
   107 
       
   108     void runTest(Stream<Path> inputs) {
       
   109         JavacTool tool = JavacTool.create();
       
   110         try (JavacFileManager fm = tool.getStandardFileManager(null, null, null)) {
       
   111             Path classes = Paths.get(System.getProperty("test.classes"));
       
   112             Iterable<? extends JavaFileObject> reconFiles =
       
   113                     fm.getJavaFileObjectsFromFiles(inputs.sorted().map(p -> p.toFile()) :: iterator);
       
   114             List<String> options = Arrays.asList("-classpath", classes.toAbsolutePath().toString());
       
   115             JavacTask reconTask = tool.getTask(null, fm, null, options, null, reconFiles);
       
   116             Iterable<? extends CompilationUnitTree> reconUnits = reconTask.parse();
       
   117             JavacTrees reconTrees = JavacTrees.instance(reconTask);
       
   118             SearchAnnotations scanner = new SearchAnnotations(reconTrees,
       
   119                                                               reconTask.getElements());
       
   120             List<JavaFileObject> validateFiles = new ArrayList<>();
       
   121 
       
   122             reconTask.analyze();
       
   123             scanner.scan(reconUnits, null);
       
   124 
       
   125             for (CompilationUnitTree cut : reconUnits) {
       
   126                 validateFiles.add(ClearAnnotations.clearAnnotations(reconTrees, cut));
       
   127             }
       
   128 
       
   129             Context validateContext = new Context();
       
   130             TestDependencies.preRegister(validateContext);
       
   131             JavacTask validateTask =
       
   132                     tool.getTask(null, fm, null, options, null, validateFiles, validateContext);
       
   133 
       
   134             validateTask.analyze();
       
   135 
       
   136             TestDependencies deps = (TestDependencies) Dependencies.instance(validateContext);
       
   137 
       
   138             if (!scanner.topLevel2Expected.equals(deps.topLevel2Completing)) {
       
   139                 throw new IllegalStateException(  "expected=" + scanner.topLevel2Expected +
       
   140                                                 "; actual=" + deps.topLevel2Completing);
       
   141             }
       
   142         } catch (IOException ex) {
       
   143             throw new IllegalStateException(ex);
       
   144         } finally {
       
   145             inputs.close();
       
   146         }
       
   147     }
       
   148 
       
   149     static final class TestDependencies extends Dependencies {
       
   150 
       
   151         public static void preRegister(Context context) {
       
   152             context.put(dependenciesKey, (Factory<Dependencies>) TestDependencies :: new);
       
   153         }
       
   154 
       
   155         public TestDependencies(Context context) {
       
   156             super(context);
       
   157         }
       
   158 
       
   159         final Stack<PhaseDescription> inProcess = new Stack<>();
       
   160 
       
   161         String topLevelMemberEnter;
       
   162         Map<String, Set<PhaseDescription>> topLevel2Completing = new HashMap<>();
       
   163 
       
   164         @Override
       
   165         public void push(ClassSymbol s, CompletionCause phase) {
       
   166             String flatname = s.flatName().toString();
       
   167             for (Phase p : Phase.values()) {
       
   168                 if (phase == p.cause) {
       
   169                     inProcess.push(new PhaseDescription(flatname, p));
       
   170                     return ;
       
   171                 }
       
   172             }
       
   173             if (phase == CompletionCause.MEMBER_ENTER) {
       
   174                 if (inProcess.isEmpty()) {
       
   175                     topLevelMemberEnter = flatname;
       
   176                 } else {
       
   177                     for (PhaseDescription running : inProcess) {
       
   178                         if (running == null)
       
   179                             continue;
       
   180 
       
   181                         Set<PhaseDescription> completing =
       
   182                                 topLevel2Completing.computeIfAbsent(running.flatname, $ -> new HashSet<>());
       
   183 
       
   184                         completing.add(new PhaseDescription(flatname, running.phase));
       
   185                     }
       
   186                 }
       
   187             }
       
   188             inProcess.push(null);
       
   189         }
       
   190 
       
   191         @Override
       
   192         public void push(AttributionKind ak, JCTree t) {
       
   193             inProcess.push(null);
       
   194         }
       
   195 
       
   196         @Override
       
   197         public void pop() {
       
   198             inProcess.pop();
       
   199         }
       
   200 
       
   201     }
       
   202 
       
   203     static final class SearchAnnotations extends TreePathScanner<Void, Void> {
       
   204         final Trees trees;
       
   205         final Elements elements;
       
   206         final TypeElement triggersCompleteAnnotation;
       
   207         final TypeElement triggersCompleteRepeatAnnotation;
       
   208         final Map<String, Set<PhaseDescription>> topLevel2Expected =
       
   209                 new HashMap<>();
       
   210 
       
   211         public SearchAnnotations(Trees trees, Elements elements) {
       
   212             this.trees = trees;
       
   213             this.elements = elements;
       
   214             this.triggersCompleteAnnotation =
       
   215                     elements.getTypeElement(TriggersComplete.class.getName());
       
   216             this.triggersCompleteRepeatAnnotation =
       
   217                     elements.getTypeElement(TriggersCompleteRepeat.class.getName());
       
   218         }
       
   219 
       
   220         @Override
       
   221         public Void visitClass(ClassTree node, Void p) {
       
   222             TypeElement te = (TypeElement) trees.getElement(getCurrentPath());
       
   223             Set<PhaseDescription> expected = new HashSet<>();
       
   224 
       
   225             for (AnnotationMirror am : getTriggersCompleteAnnotation(te)) {
       
   226                 TypeMirror of = (TypeMirror) findAttribute(am, "of").getValue();
       
   227                 Name ofName = elements.getBinaryName((TypeElement) ((DeclaredType) of).asElement());
       
   228                 Element at = (Element) findAttribute(am, "at").getValue();
       
   229                 Phase phase = Phase.valueOf(at.getSimpleName().toString());
       
   230                 expected.add(new PhaseDescription(ofName.toString(), phase));
       
   231             }
       
   232 
       
   233             if (!expected.isEmpty())
       
   234                 topLevel2Expected.put(elements.getBinaryName(te).toString(), expected);
       
   235 
       
   236             return super.visitClass(node, p);
       
   237         }
       
   238 
       
   239         Collection<AnnotationMirror> getTriggersCompleteAnnotation(TypeElement te) {
       
   240             for (AnnotationMirror am : te.getAnnotationMirrors()) {
       
   241                 if (triggersCompleteAnnotation.equals(am.getAnnotationType().asElement())) {
       
   242                     return Collections.singletonList(am);
       
   243                 }
       
   244                 if (triggersCompleteRepeatAnnotation.equals(am.getAnnotationType().asElement())) {
       
   245                     return (Collection<AnnotationMirror>) findAttribute(am, "value").getValue();
       
   246                 }
       
   247             }
       
   248             return Collections.emptyList();
       
   249         }
       
   250 
       
   251         AnnotationValue findAttribute(AnnotationMirror mirror, String name) {
       
   252             for (Entry<? extends ExecutableElement, ? extends AnnotationValue> e :
       
   253                     mirror.getElementValues().entrySet()) {
       
   254                 if (e.getKey().getSimpleName().contentEquals(name)) {
       
   255                     return e.getValue();
       
   256                 }
       
   257             }
       
   258 
       
   259             throw new IllegalStateException("Could not find " + name + " in " + mirror);
       
   260         }
       
   261     }
       
   262 
       
   263     static final class ClearAnnotations extends TreePathScanner<Void, Void> {
       
   264         final SourcePositions positions;
       
   265         final List<int[]> spans2Clear = new ArrayList<>();
       
   266 
       
   267         ClearAnnotations(Trees trees) {
       
   268             this.positions = trees.getSourcePositions();
       
   269         }
       
   270 
       
   271         @Override
       
   272         public Void visitAnnotation(AnnotationTree node, Void p) {
       
   273             removeCurrentNode();
       
   274             return null;
       
   275         }
       
   276 
       
   277         @Override
       
   278         public Void visitImport(ImportTree node, Void p) {
       
   279             if (node.getQualifiedIdentifier().toString().startsWith("annotations.")) {
       
   280                 removeCurrentNode();
       
   281                 return null;
       
   282             }
       
   283             return super.visitImport(node, p);
       
   284         }
       
   285 
       
   286         void removeCurrentNode() {
       
   287             CompilationUnitTree topLevel = getCurrentPath().getCompilationUnit();
       
   288             Tree node = getCurrentPath().getLeaf();
       
   289             spans2Clear.add(new int[] {(int) positions.getStartPosition(topLevel, node),
       
   290                                        (int) positions.getEndPosition(topLevel, node)});
       
   291         }
       
   292 
       
   293         static JavaFileObject clearAnnotations(Trees trees, CompilationUnitTree cut)
       
   294                 throws IOException {
       
   295             ClearAnnotations a = new ClearAnnotations(trees);
       
   296             a.scan(cut, null);
       
   297             Collections.sort(a.spans2Clear, (s1, s2) -> s2[0] - s1[0]);
       
   298             StringBuilder result = new StringBuilder(cut.getSourceFile().getCharContent(true));
       
   299             for (int[] toClear : a.spans2Clear) {
       
   300                 result.delete(toClear[0], toClear[1]);
       
   301             }
       
   302             return new TestJavaFileObject(cut.getSourceFile().toUri(), result.toString());
       
   303         }
       
   304 
       
   305     }
       
   306 
       
   307     static final class PhaseDescription {
       
   308         final String flatname;
       
   309         final Phase phase;
       
   310 
       
   311         public PhaseDescription(String flatname, Phase phase) {
       
   312             this.flatname = flatname;
       
   313             this.phase = phase;
       
   314         }
       
   315 
       
   316         @Override
       
   317         public String toString() {
       
   318             return "@annotations.TriggersComplete(of=" + flatname + ".class," +
       
   319                    "at=annotations.Phase." + phase + ')';
       
   320         }
       
   321 
       
   322         @Override
       
   323         public int hashCode() {
       
   324             int hash = 7;
       
   325             hash = 89 * hash + Objects.hashCode(this.flatname);
       
   326             hash = 89 * hash + Objects.hashCode(this.phase);
       
   327             return hash;
       
   328         }
       
   329 
       
   330         @Override
       
   331         public boolean equals(Object obj) {
       
   332             if (obj == null) {
       
   333                 return false;
       
   334             }
       
   335             if (getClass() != obj.getClass()) {
       
   336                 return false;
       
   337             }
       
   338             final PhaseDescription other = (PhaseDescription) obj;
       
   339             if (!Objects.equals(this.flatname, other.flatname)) {
       
   340                 return false;
       
   341             }
       
   342             if (this.phase != other.phase) {
       
   343                 return false;
       
   344             }
       
   345             return true;
       
   346         }
       
   347 
       
   348     }
       
   349 
       
   350     static final class TestJavaFileObject extends SimpleJavaFileObject {
       
   351         private final String content;
       
   352 
       
   353         public TestJavaFileObject(URI uri, String content) {
       
   354             super(uri, Kind.SOURCE);
       
   355             this.content = content;
       
   356         }
       
   357 
       
   358         @Override
       
   359         public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
       
   360             return content;
       
   361         }
       
   362 
       
   363     }
       
   364 }
       
   365 
       
   366