src/jdk.compiler/share/classes/com/sun/tools/sjavac/comp/dependencies/DependencyScanner.java

Print this page
rev 2819 : imported patch my-classpath-deps-00


   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 package com.sun.tools.sjavac.comp.dependencies;
  26 

  27 import java.util.HashSet;

  28 import java.util.Set;
  29 
  30 import com.sun.tools.javac.code.Symbol;
  31 import com.sun.tools.javac.code.Symbol.ClassSymbol;
  32 import com.sun.tools.javac.code.Symbol.PackageSymbol;

  33 import com.sun.tools.javac.code.Type;
  34 import com.sun.tools.javac.code.TypeTag;

  35 import com.sun.tools.javac.tree.JCTree.JCFieldAccess;
  36 import com.sun.tools.javac.tree.JCTree.JCIdent;


  37 import com.sun.tools.javac.tree.TreeScanner;



  38 
  39 class DependencyScanner extends TreeScanner {
  40 
  41     public final Set<Dependency> dependencies = new HashSet<>();












  42 
  43     private boolean isValidDependency(Type t) {
  44         if (t == null || t.isPrimitiveOrVoid() || t.isErroneous())
  45             return false;


  46         TypeTag tag = t.getTag();
  47         return tag != TypeTag.PACKAGE
  48             && tag != TypeTag.METHOD
  49             && tag != TypeTag.ARRAY
  50             && tag != TypeTag.TYPEVAR;
  51     }
  52 
  53     @Override




























  54     public void visitIdent(JCIdent tree) {
  55         if (isValidDependency(tree.type))
  56             dependencies.add(new TypeAndSupertypesDependency(tree.type.tsym));
  57         super.visitIdent(tree);
  58     }
  59 
  60     @Override



  61     public void visitSelect(JCFieldAccess tree) {
  62         if (tree.getIdentifier().contentEquals("*")) {
  63             Symbol sym = tree.selected instanceof JCIdent ? ((JCIdent) tree.selected).sym
  64                                                           : ((JCFieldAccess) tree.selected).sym;
  65             if (sym instanceof ClassSymbol) {
  66                 ClassSymbol clsSym = (ClassSymbol) sym;
  67                 dependencies.add(new TypeAndSupertypesDependency(clsSym.type.tsym));

  68             } else {
  69                 dependencies.add(new PackageDependency((PackageSymbol) sym));
  70             }
  71         } else if (tree.type != null && tree.type.hasTag(TypeTag.METHOD)) {  // Method call? Depend on the result (even though we never access it elsewhere)






















  72             Type retType = tree.type.getReturnType();
  73             if (isValidDependency(retType))
  74                 dependencies.add(new TypeAndSupertypesDependency(retType.tsym));
  75         } else if (isValidDependency(tree.type)) {
  76             dependencies.add(new TypeAndSupertypesDependency(tree.type.tsym));


  77         }
  78         super.visitSelect(tree);
  79     }
  80 
  81     public Set<Dependency> getResult() {
  82         return dependencies;

















  83     }





  84 }


   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 package com.sun.tools.sjavac.comp.dependencies;
  26 import java.util.Collections;
  27 import java.util.HashMap;
  28 import java.util.HashSet;
  29 import java.util.Map;
  30 import java.util.Set;
  31 
  32 import com.sun.tools.javac.code.Symbol;
  33 import com.sun.tools.javac.code.Symbol.ClassSymbol;
  34 import com.sun.tools.javac.code.Symbol.PackageSymbol;
  35 import com.sun.tools.javac.code.Symtab;
  36 import com.sun.tools.javac.code.Type;
  37 import com.sun.tools.javac.code.TypeTag;
  38 import com.sun.tools.javac.tree.JCTree.JCClassDecl;
  39 import com.sun.tools.javac.tree.JCTree.JCFieldAccess;
  40 import com.sun.tools.javac.tree.JCTree.JCIdent;
  41 import com.sun.tools.javac.tree.JCTree.JCImport;
  42 import com.sun.tools.javac.tree.JCTree.JCPackageDecl;
  43 import com.sun.tools.javac.tree.TreeScanner;
  44 import com.sun.tools.javac.util.Assert;
  45 import com.sun.tools.javac.util.Context;
  46 import com.sun.tools.sjavac.Util;
  47 
  48 class DependencyScanner extends TreeScanner {
  49 
  50     private final ClassSymbol OBJECT_SYM;
  51     private String currentClass = null;
  52 
  53     public final Map<String, Set<ClassSymbol>> dependencies = new HashMap<>();
  54     public final Map<String, Set<PackageSymbol>> lazyImports = new HashMap<>();
  55 
  56     // Keep track of imported classes, since they should be reported as dependency of any subsequent class decls. (This may change when moving to CompilationUnits).
  57     private final Set<ClassSymbol> typesImported = new HashSet<>();
  58     private final Set<PackageSymbol> lazyImportedPkgs = new HashSet<>();
  59 
  60     public DependencyScanner(Context context) {
  61         OBJECT_SYM = (ClassSymbol) Symtab.instance(context).objectType.tsym;
  62     }
  63 
  64     private boolean isValidDependency(Type t) {
  65         if (t == null || t.isPrimitiveOrVoid() || t.isErroneous())
  66             return false;
  67         if (!(t.tsym instanceof ClassSymbol))
  68             return false;
  69         TypeTag tag = t.getTag();
  70         return tag != TypeTag.PACKAGE
  71             && tag != TypeTag.METHOD
  72             && tag != TypeTag.ARRAY
  73             && tag != TypeTag.TYPEVAR;
  74     }
  75 
  76     @Override
  77     public void visitClassDef(JCClassDecl tree) {
  78         // Skip anonymous classes for now. (Not sure if non-top level classes
  79         // are interesting at this point)
  80         if (tree.sym == null)
  81             return;
  82 
  83         String prevClass = currentClass;
  84         currentClass = tree.sym.flatname.toString();
  85         // Make sure the currentClass depends on the imported stuff. (This may go away when moving to CompilationUnits.)
  86         typesImported.forEach(this::addDep);
  87         lazyImports.put(currentClass, lazyImportedPkgs);
  88 
  89         // Make sure current class depends on Object if no extends expression is provided
  90         if (tree.extending == null)
  91             addDep(OBJECT_SYM);
  92         else
  93             addAllDeps(allSupertypes((ClassSymbol) tree.extending.type.tsym));
  94         super.visitClassDef(tree);
  95         currentClass = prevClass;
  96     }
  97 
  98     @Override
  99     public void visitPackageDef(JCPackageDecl tree) {
 100         // Ignore identifiers found in the package declaration.
 101         //super.visitPackageDef(tree);
 102     }
 103 
 104     @Override
 105     public void visitIdent(JCIdent tree) {
 106         if (isValidDependency(tree.type))
 107             addDep((ClassSymbol) tree.type.tsym);
 108         super.visitIdent(tree);
 109     }
 110 
 111     @Override
 112     public void visitImport(JCImport inport) {
 113         TreeScanner importVisitor = new TreeScanner() {
 114             @Override
 115             public void visitSelect(JCFieldAccess tree) {
 116                 if (tree.getIdentifier().contentEquals("*")) {
 117                     Symbol sym = tree.selected instanceof JCIdent ? ((JCIdent) tree.selected).sym
 118                                                                   : ((JCFieldAccess) tree.selected).sym;
 119                     if (sym instanceof ClassSymbol) {
 120                         typesImported.add((ClassSymbol) sym);
 121                     } else if (sym instanceof PackageSymbol) {
 122                         lazyImports.merge(currentClass, Collections.singleton((PackageSymbol) sym), Util::union);
 123                     } else {
 124                         Assert.error("Unknown import symbol: " + sym);
 125                     }
 126                 } else {
 127                     // If this is a static import, the sym is null (why?) so
 128                     // overapproximate by going up one level.
 129                     if (inport.isStatic())
 130                         tree = ((JCFieldAccess) tree.selected);
 131                     typesImported.add((ClassSymbol) tree.sym);
 132                 }
 133             }
 134         };
 135         inport.accept(importVisitor);
 136 
 137         // Avoid further visitSelect etc since we have no currentClass.
 138         // super.visitImport(tree);
 139     }
 140 
 141     @Override
 142     public void visitSelect(JCFieldAccess tree) {
 143 
 144         // This is not entirely safe. @MyAnno(SomeClass.class) public class C {} should depend on SomeClass.class
 145         if (currentClass == null)
 146             return;
 147 
 148         if (tree.type != null && tree.type.hasTag(TypeTag.METHOD)) {  // Method call? Depend on the result (even though we never access it elsewhere)
 149             Type retType = tree.type.getReturnType();
 150             if (isValidDependency(retType))
 151                 addDep((ClassSymbol) retType.tsym);
 152         } else if (isValidDependency(tree.type)) {
 153             ClassSymbol cs = (ClassSymbol) tree.type.tsym;
 154             addDep(cs);
 155             addAllDeps(allSupertypes(cs));
 156         }
 157         super.visitSelect(tree);
 158     }
 159 
 160     private Set<ClassSymbol> allSupertypes(ClassSymbol cs) {
 161         if (cs == null)
 162             return Collections.emptySet();
 163         Set<ClassSymbol> result = new HashSet<>();
 164         result.add(cs);
 165         if (cs instanceof ClassSymbol) {
 166             result.addAll(allSupertypes((ClassSymbol) cs.getSuperclass().tsym));
 167             for (Type it : cs.getInterfaces())
 168                 result.addAll(allSupertypes((ClassSymbol) it.tsym));
 169         }
 170         return result;
 171     }
 172 
 173     private void addDep(ClassSymbol tsym) {
 174         if (currentClass == null)
 175             throw new RuntimeException("currentClass == null");
 176         if (tsym == null)
 177             throw new RuntimeException("sym == null");
 178         dependencies.merge(currentClass, Collections.singleton(tsym), Util::union);
 179     }
 180 
 181     private void addAllDeps(Set<ClassSymbol> tsyms) {
 182         dependencies.merge(currentClass, tsyms, Util::union);
 183     }
 184 
 185 }