diff --git a/cycle_finder/Makefile b/cycle_finder/Makefile index 50fcd85eac..46b7dd196e 100644 --- a/cycle_finder/Makefile +++ b/cycle_finder/Makefile @@ -41,8 +41,7 @@ JAVA_SOURCES = \ com/google/devtools/cyclefinder/NameList.java \ com/google/devtools/cyclefinder/Options.java \ com/google/devtools/cyclefinder/ReferenceGraph.java \ - com/google/devtools/cyclefinder/Tarjans.java \ - com/google/devtools/cyclefinder/TypeCollector.java + com/google/devtools/cyclefinder/Tarjans.java RESOURCES = \ com/google/devtools/cyclefinder/CycleFinder.properties \ diff --git a/cycle_finder/src/main/java/com/google/devtools/cyclefinder/CycleFinder.java b/cycle_finder/src/main/java/com/google/devtools/cyclefinder/CycleFinder.java index d0e35a4899..10e1ceb392 100644 --- a/cycle_finder/src/main/java/com/google/devtools/cyclefinder/CycleFinder.java +++ b/cycle_finder/src/main/java/com/google/devtools/cyclefinder/CycleFinder.java @@ -129,9 +129,10 @@ private File stripIncompatible( } public List> findCycles() throws IOException { - final TypeCollector typeCollector = new TypeCollector(); Parser parser = createParser(options); final CaptureFields captureFields = new CaptureFields(); + final GraphBuilder graphBuilder = + new GraphBuilder(captureFields, NameList.createFromFiles(options.getWhitelistFiles())); List sourceFiles = options.getSourceFiles(); File strippedDir = stripIncompatible(sourceFiles, parser); @@ -140,7 +141,7 @@ public List> findCycles() throws IOException { @Override public void handleParsedUnit(String path, CompilationUnit unit) { new LambdaTypeElementAdder(unit).run(); - typeCollector.visitAST(unit); + graphBuilder.visitAST(unit); new OuterReferenceResolver(unit).run(); captureFields.collect(unit); } @@ -154,10 +155,7 @@ public void handleParsedUnit(String path, CompilationUnit unit) { } // Construct the graph and find cycles. - ReferenceGraph graph = new GraphBuilder( - typeCollector, captureFields, NameList.createFromFiles(options.getWhitelistFiles())) - .constructGraph() - .getGraph(); + ReferenceGraph graph = graphBuilder.constructGraph().getGraph(); for (ReferenceGraph component : graph.getStronglyConnectedComponents(getSeedNodes(graph))) { handleStronglyConnectedComponent(component); } diff --git a/cycle_finder/src/main/java/com/google/devtools/cyclefinder/GraphBuilder.java b/cycle_finder/src/main/java/com/google/devtools/cyclefinder/GraphBuilder.java index 5998a0200d..b69bfd469f 100644 --- a/cycle_finder/src/main/java/com/google/devtools/cyclefinder/GraphBuilder.java +++ b/cycle_finder/src/main/java/com/google/devtools/cyclefinder/GraphBuilder.java @@ -14,13 +14,21 @@ package com.google.devtools.cyclefinder; +import com.google.common.base.Strings; import com.google.common.collect.HashMultimap; import com.google.common.collect.ImmutableList; import com.google.common.collect.Multimap; import com.google.common.collect.SetMultimap; import com.google.common.collect.Sets; +import com.google.devtools.j2objc.ast.AnonymousClassDeclaration; +import com.google.devtools.j2objc.ast.ClassInstanceCreation; +import com.google.devtools.j2objc.ast.CompilationUnit; +import com.google.devtools.j2objc.ast.MethodInvocation; +import com.google.devtools.j2objc.ast.TreeVisitor; +import com.google.devtools.j2objc.ast.TypeDeclaration; import com.google.devtools.j2objc.jdt.BindingConverter; import com.google.devtools.j2objc.util.ElementUtil; +import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; @@ -38,14 +46,12 @@ */ public class GraphBuilder { - private final Map allTypes; + private final Map allTypes = new HashMap<>(); private final CaptureFields captureFields; private final NameList whitelist; private final ReferenceGraph graph = new ReferenceGraph(); - public GraphBuilder( - TypeCollector typeCollector, CaptureFields captureFields, NameList whitelist) { - this.allTypes = typeCollector.getTypes(); + public GraphBuilder(CaptureFields captureFields, NameList whitelist) { this.captureFields = captureFields; this.whitelist = whitelist; } @@ -70,6 +76,41 @@ private void addEdge(Edge e) { } } + private void addNode(TypeNode node) { + allTypes.put(node.getSignature(), node); + } + + private void visitType(ITypeBinding type) { + if (type == null) { + return; + } + type = getElementType(type); + if (allTypes.containsKey(type.getKey()) || type.isPrimitive() || type.isRawType()) { + return; + } + if (hasNestedWildcard(type)) { + // Avoid infinite recursion caused by nested wildcard types. + return; + } + addNode(new TypeNode(type, getNameForType(type))); + followType(type); + } + + private void followType(ITypeBinding type) { + visitType(type.getSuperclass()); + visitType(type.getDeclaringClass()); + for (IVariableBinding field : type.getDeclaredFields()) { + ITypeBinding fieldType = field.getType(); + for (ITypeBinding typeParam : fieldType.getTypeArguments()) { + visitType(typeParam); + } + visitType(fieldType); + } + for (ITypeBinding interfaze : type.getInterfaces()) { + visitType(interfaze); + } + } + private void addFieldEdges() { for (TypeNode node : allTypes.values()) { ITypeBinding type = node.getTypeBinding(); @@ -195,4 +236,67 @@ private void addAnonymousClassCaptureEdges() { } } } + + private static String getNameForType(ITypeBinding type) { + String name = type.getName(); + return Strings.isNullOrEmpty(name) ? type.getKey() : name; + } + + private static boolean hasWildcard(ITypeBinding type) { + if (type.isWildcardType()) { + return true; + } + for (ITypeBinding typeParam : type.getTypeArguments()) { + if (hasWildcard(typeParam)) { + return true; + } + } + return false; + } + + private static boolean hasNestedWildcard(ITypeBinding type) { + ITypeBinding bound = type.getBound(); + if (bound != null && hasWildcard(bound)) { + return true; + } + for (ITypeBinding typeParam : type.getTypeArguments()) { + if (hasNestedWildcard(typeParam)) { + return true; + } + } + return false; + } + + public void visitAST(final CompilationUnit unit) { + unit.accept(new TreeVisitor() { + + @Override + public boolean visit(TypeDeclaration node) { + ITypeBinding binding = BindingConverter.unwrapTypeElement(node.getTypeElement()); + addNode(new TypeNode(binding, getNameForType(binding))); + followType(binding); + return true; + } + + @Override + public boolean visit(AnonymousClassDeclaration node) { + ITypeBinding binding = BindingConverter.unwrapTypeElement(node.getTypeElement()); + addNode(new TypeNode(binding, "anonymous:" + node.getLineNumber())); + followType(binding); + return true; + } + + @Override + public boolean visit(ClassInstanceCreation node) { + visitType(BindingConverter.unwrapTypeMirrorIntoTypeBinding(node.getTypeMirror())); + return true; + } + + @Override + public boolean visit(MethodInvocation node) { + visitType(BindingConverter.unwrapTypeMirrorIntoTypeBinding(node.getTypeMirror())); + return true; + } + }); + } } diff --git a/cycle_finder/src/main/java/com/google/devtools/cyclefinder/TypeCollector.java b/cycle_finder/src/main/java/com/google/devtools/cyclefinder/TypeCollector.java deleted file mode 100644 index 5d9bc3479b..0000000000 --- a/cycle_finder/src/main/java/com/google/devtools/cyclefinder/TypeCollector.java +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.devtools.cyclefinder; - -import com.google.common.base.Strings; -import com.google.devtools.j2objc.ast.AnonymousClassDeclaration; -import com.google.devtools.j2objc.ast.ClassInstanceCreation; -import com.google.devtools.j2objc.ast.CompilationUnit; -import com.google.devtools.j2objc.ast.MethodInvocation; -import com.google.devtools.j2objc.ast.TreeVisitor; -import com.google.devtools.j2objc.ast.TypeDeclaration; -import com.google.devtools.j2objc.jdt.BindingConverter; -import java.util.HashMap; -import java.util.Map; -import org.eclipse.jdt.core.dom.ITypeBinding; -import org.eclipse.jdt.core.dom.IVariableBinding; - -/** - * Recursively visits links between type bindings, collecting all reachable - * types. - */ -class TypeCollector { - - private Map allTypes = new HashMap<>(); - - private static Map renamings = new HashMap<>(); - - public Map getTypes() { - return allTypes; - } - - private static String getNameForType(ITypeBinding type) { - String name = renamings.get(type); - if (name != null) { - return name; - } - name = type.getName(); - if (!Strings.isNullOrEmpty(name)) { - return name; - } - return type.getKey(); - } - - public void visitType(ITypeBinding type) { - if (type == null) { - return; - } - type = getElementType(type); - if (allTypes.containsKey(type.getKey()) || type.isPrimitive() || type.isRawType()) { - return; - } - if (hasNestedWildcard(type)) { - // Avoid infinite recursion caused by nested wildcard types. - return; - } - allTypes.put(type.getKey(), new TypeNode(type, getNameForType(type))); - visitType(type.getSuperclass()); - visitType(type.getDeclaringClass()); - for (IVariableBinding field : type.getDeclaredFields()) { - ITypeBinding fieldType = field.getType(); - for (ITypeBinding typeParam : fieldType.getTypeArguments()) { - visitType(typeParam); - } - visitType(fieldType); - } - for (ITypeBinding interfaze : type.getInterfaces()) { - visitType(interfaze); - } - } - - private static boolean hasWildcard(ITypeBinding type) { - if (type.isWildcardType()) { - return true; - } - for (ITypeBinding typeParam : type.getTypeArguments()) { - if (hasWildcard(typeParam)) { - return true; - } - } - return false; - } - - private static boolean hasNestedWildcard(ITypeBinding type) { - ITypeBinding bound = type.getBound(); - if (bound != null && hasWildcard(bound)) { - return true; - } - for (ITypeBinding typeParam : type.getTypeArguments()) { - if (hasNestedWildcard(typeParam)) { - return true; - } - } - return false; - } - - public void visitAST(final CompilationUnit unit) { - unit.accept(new TreeVisitor() { - @Override - public boolean visit(TypeDeclaration node) { - visitType(BindingConverter.unwrapTypeElement(node.getTypeElement())); - return true; - } - @Override - public boolean visit(AnonymousClassDeclaration node) { - ITypeBinding binding = BindingConverter.unwrapTypeElement(node.getTypeElement()); - renamings.put(binding, "anonymous:" + node.getLineNumber()); - visitType(binding); - return true; - } - @Override - public boolean visit(ClassInstanceCreation node) { - visitType(BindingConverter.unwrapTypeMirrorIntoTypeBinding(node.getTypeMirror())); - return true; - } - @Override - public boolean visit(MethodInvocation node) { - visitType(BindingConverter.unwrapTypeMirrorIntoTypeBinding(node.getTypeMirror())); - return true; - } - }); - } - - private ITypeBinding getElementType(ITypeBinding type) { - if (type.isArray()) { - return type.getElementType(); - } - return type; - } -}