diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/ClassGraph.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/ClassGraph.java new file mode 100644 index 0000000000000000000000000000000000000000..0777a79d102ca3ed772758b0822864b03a64e057 --- /dev/null +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/ClassGraph.java @@ -0,0 +1,237 @@ +package es.upv.mist.slicing.graphs; + +import com.github.javaparser.ast.CompilationUnit; +import com.github.javaparser.ast.NodeList; +import com.github.javaparser.ast.body.*; +import com.github.javaparser.ast.visitor.VoidVisitorAdapter; +import es.upv.mist.slicing.arcs.Arc; +import es.upv.mist.slicing.utils.ASTUtils; +import es.upv.mist.slicing.utils.Utils; +import org.jgrapht.graph.DefaultEdge; +import org.jgrapht.graph.DirectedPseudograph; +import org.jgrapht.nio.Attribute; +import org.jgrapht.nio.DefaultAttribute; +import org.jgrapht.nio.dot.DOTExporter; + +import java.util.*; + +public class ClassGraph extends DirectedPseudograph implements Buildable> { + + /** The key of the vertex map needs to be a String because extendedTypes represent extended classes + * as ClassOrInterfaceType objects while class declarations define classes as ClassOrInterfaceDeclaration + * objects and there is no relationship to match them */ + private final Map vertexDeclarationMap = new HashMap<>(); + + private boolean built = false; + + public ClassGraph() { + super(null, null, false); + } + + @Override + public void build(NodeList arg) { + if (isBuilt()) + return; + buildVertices(arg); + buildEdges(arg); + built = true; + } + + @Override + public boolean isBuilt() { + return built; + } + + /** Find the class declarations, the field declaration, and method and constructor declarations (vertices) + * in the given list of compilation units. */ + protected void buildVertices(NodeList arg) { + arg.accept(new VoidVisitorAdapter() { + private final Deque classStack = new LinkedList<>(); +// QUESTIONS & LACKS: +// 1) Is it necessary to include something apart from class vertices? +// 2) Private classes inside other classes? +// 3) Static declaration blocks not considered + + @Override + public void visit(ClassOrInterfaceDeclaration n, Void arg) { + classStack.push(n); + addClassDeclaration(n); + super.visit(n, arg); + classStack.pop(); + } + + @Override + public void visit(FieldDeclaration n, Void arg) { + addFieldDeclaration(n, classStack.peek()); + } + + @Override + public void visit(MethodDeclaration n, Void arg) { + addCallableDeclaration(n, classStack.peek()); + } + + @Override + public void visit(ConstructorDeclaration n, Void arg) { + addCallableDeclaration(n, classStack.peek()); + } + }, null); + } + + /** Add a class declaration vertex to the class graph */ + protected void addClassDeclaration(ClassOrInterfaceDeclaration n) { + ClassGraph.Vertex v = new ClassGraph.Vertex(n); + // Required string to match ClassOrInterfaceType and ClassOrInterfaceDeclaration. QualifiedName Not Valid + vertexDeclarationMap.put(n.getNameAsString(), v); + addVertex(v); + } + + /** Add a field declaration vertex to the class graph */ + protected void addFieldDeclaration(FieldDeclaration n, ClassOrInterfaceDeclaration c){ + ClassGraph.Vertex v = new ClassGraph.Vertex(n); + // Key value: hashCode + toString() to avoid multiple field declarations with the same syntax in different classes + vertexDeclarationMap.put(c.getFullyQualifiedName().get()+ "." + n.toString(), v); + addVertex(v); + } + + /** Add a method/constructor declaration vertex to the class graph */ + protected void addCallableDeclaration(CallableDeclaration n, ClassOrInterfaceDeclaration c){ + assert n instanceof ConstructorDeclaration || n instanceof MethodDeclaration; + ClassGraph.Vertex v = new ClassGraph.Vertex(n); + vertexDeclarationMap.put(c.getFullyQualifiedName().get()+ "." + n.getSignature().toString(), v); + addVertex(v); + } + + /** Find the class declarations, field declarations, and method declarations and build the corresponding + * member/extends/implements relationships in the given list of compilation units. */ + protected void buildEdges(NodeList arg) { + arg.accept(new VoidVisitorAdapter() { + private final Deque classStack = new LinkedList<>(); + + @Override + public void visit(ClassOrInterfaceDeclaration n, Void arg) { + classStack.push(n); + Vertex v = vertexDeclarationMap.get(n.getNameAsString()); + addClassEdges(v); + super.visit(n, arg); + classStack.pop(); + } + + @Override + public void visit(FieldDeclaration n, Void arg) { + ClassOrInterfaceDeclaration clazz = classStack.peek(); + Vertex c = vertexDeclarationMap.get(clazz.getNameAsString()); + Vertex v = vertexDeclarationMap.get(clazz.getFullyQualifiedName().get()+ "." + n.toString()); + addEdge(c, v, new MemberArc()); + } + + @Override + public void visit(MethodDeclaration n, Void arg) { + ClassOrInterfaceDeclaration clazz = classStack.peek(); + Vertex c = vertexDeclarationMap.get(clazz.getNameAsString()); + Vertex v = vertexDeclarationMap.get(clazz.getFullyQualifiedName().get()+ "." + n.getSignature().toString()); + addEdge(c, v, new MemberArc()); + } + + @Override + public void visit(ConstructorDeclaration n, Void arg) { + ClassOrInterfaceDeclaration clazz = classStack.peek(); + Vertex c = vertexDeclarationMap.get(clazz.getNameAsString()); + Vertex v = vertexDeclarationMap.get(clazz.getFullyQualifiedName().get()+ "." + n.getSignature().toString()); + addEdge(c, v, new MemberArc()); + } + }, null); + } + + protected void addClassEdges(Vertex v){ + assert v.declaration instanceof ClassOrInterfaceDeclaration; + ClassOrInterfaceDeclaration dv = (ClassOrInterfaceDeclaration) v.declaration; + dv.getExtendedTypes().forEach(p -> { + Vertex source = vertexDeclarationMap.get(p.getNameAsString()); + if (source != null && containsVertex(v)) + addEdge(source, v, new ExtendsArc()); + }); + dv.getImplementedTypes().forEach(p -> { + Vertex source = vertexDeclarationMap.get(p.getNameAsString()); + if (source != null && containsVertex(v)) + addEdge(source, v, new ImplementsArc()); + }); + } + + /** Creates a graph-appropriate DOT exporter. */ + public DOTExporter, CallGraph.Edge> getDOTExporter() { + DOTExporter, CallGraph.Edge> dot = new DOTExporter<>(); + dot.setVertexAttributeProvider(decl -> Utils.dotLabel(decl.getDeclarationAsString(false, false, false))); + dot.setEdgeAttributeProvider(edge -> Utils.dotLabel(edge.getCall().toString())); + return dot; + } + + /** A vertex containing the declaration it represents. It only exists because + * JGraphT relies heavily on equals comparison, which may not be correct in declarations. */ + public static class Vertex { + // First ancestor common class in the JavaParser hierarchy for + // ClassOrInterfaceDeclaration, FieldDeclaration and CallableDeclaration + protected final BodyDeclaration declaration; + + public Vertex(BodyDeclaration declaration) { + this.declaration = declaration; + } + + /** The declaration represented by this node. */ + public BodyDeclaration getDeclaration() { + return declaration; + } + + @Override + public int hashCode() { + return Objects.hash(declaration, declaration.getRange()); + } + + @Override + public boolean equals(Object obj) { + return obj instanceof CallGraph.Vertex && ASTUtils.equalsWithRangeInCU(((CallGraph.Vertex) obj).declaration, declaration); + } + + @Override + public String toString() { + return super.toString(); + } + } + + /** + * An edge of the {@link ClassGraph}. Represents the inheritance relationship in Java. + * It goes from the base class to the derived class + */ + public static class ExtendsArc extends Arc { + public Map getDotAttributes() { + Map map = super.getDotAttributes(); + map.put("style", DefaultAttribute.createAttribute("dashed")); + return map; + } + } + + /** + * An edge of the {@link ClassGraph}. Represents the implements relationship in Java. + * It goes from the interface to the class that implements it. + */ + public static class ImplementsArc extends Arc { + public Map getDotAttributes() { + Map map = super.getDotAttributes(); + map.put("style", DefaultAttribute.createAttribute("dashed")); + return map; + } + } + + /** + * An edge of the {@link ClassGraph}. It represents the membership of a class node. + * It links the class node and its inner data members/function definitions. + */ + public static class MemberArc extends Arc { + public Map getDotAttributes() { + Map map = super.getDotAttributes(); + map.put("style", DefaultAttribute.createAttribute("dashed")); + return map; + } + } +} + + diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/SDG.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/SDG.java index c22cb79d639a7e73615c6fc69493b081f7b263bb..aaf67c9ba06b1308bf3ab37bb7f22be4fcea8972 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/SDG.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/SDG.java @@ -15,6 +15,7 @@ import es.upv.mist.slicing.arcs.sdg.ParameterInOutArc; import es.upv.mist.slicing.arcs.sdg.SummaryArc; import es.upv.mist.slicing.graphs.Buildable; import es.upv.mist.slicing.graphs.CallGraph; +import es.upv.mist.slicing.graphs.ClassGraph; import es.upv.mist.slicing.graphs.Graph; import es.upv.mist.slicing.graphs.cfg.CFG; import es.upv.mist.slicing.graphs.cfg.CFGBuilder; @@ -107,6 +108,7 @@ public class SDG extends Graph implements Sliceable, Buildable nodeList){ + ClassGraph classGraph = new ClassGraph(); + classGraph.build(nodeList); + return classGraph; + } + + /** Perform interprocedural analyses to determine the actual, formal and call return nodes. */ protected void dataFlowAnalysis(CallGraph callGraph) { new InterproceduralDefinitionFinder(callGraph, cfgMap).save(); // 3.1