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 }
|