Loading sdg-core/src/main/java/es/upv/mist/slicing/graphs/CallGraph.java +68 −17 Original line number Diff line number Diff line Loading @@ -2,10 +2,8 @@ package es.upv.mist.slicing.graphs; import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.NodeList; import com.github.javaparser.ast.body.CallableDeclaration; import com.github.javaparser.ast.body.ConstructorDeclaration; import com.github.javaparser.ast.body.InitializerDeclaration; import com.github.javaparser.ast.body.MethodDeclaration; import com.github.javaparser.ast.body.*; import com.github.javaparser.ast.expr.Expression; import com.github.javaparser.ast.expr.MethodCallExpr; import com.github.javaparser.ast.expr.ObjectCreationExpr; import com.github.javaparser.ast.stmt.ExplicitConstructorInvocationStmt; Loading @@ -15,16 +13,17 @@ import com.github.javaparser.resolution.declarations.ResolvedMethodLikeDeclarati import es.upv.mist.slicing.graphs.cfg.CFG; import es.upv.mist.slicing.nodes.GraphNode; import es.upv.mist.slicing.utils.ASTUtils; import es.upv.mist.slicing.utils.NodeHashSet; import es.upv.mist.slicing.utils.NodeNotFoundException; import es.upv.mist.slicing.utils.Utils; import org.jgrapht.graph.DefaultEdge; import org.jgrapht.graph.DirectedPseudograph; import org.jgrapht.nio.dot.DOTExporter; import java.util.Deque; import java.util.LinkedList; import java.util.Map; import java.util.Objects; import java.util.*; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; import java.util.stream.Stream; /** * A directed graph which displays the available method declarations as nodes and their Loading @@ -41,23 +40,23 @@ import java.util.Objects; public class CallGraph extends DirectedPseudograph<CallGraph.Vertex, CallGraph.Edge<?>> implements Buildable<NodeList<CompilationUnit>> { private final Map<CallableDeclaration<?>, CFG> cfgMap; private final Map<CallableDeclaration<?>, Vertex> vertexDeclarationMap = ASTUtils.newIdentityHashMap(); private final ClassGraph classGraph; private boolean built = false; public CallGraph(Map<CallableDeclaration<?>, CFG> cfgMap) { public CallGraph(Map<CallableDeclaration<?>, CFG> cfgMap, ClassGraph classGraph) { super(null, null, false); this.cfgMap = cfgMap; this.classGraph = classGraph; } /** Resolve a call to its declaration, by using the call AST nodes stored on the edges. */ public CallableDeclaration<?> getCallTarget(Resolvable<? extends ResolvedMethodLikeDeclaration> call) { /** Resolve a call to all its possible declarations, by using the call AST nodes stored on the edges. */ public Stream<CallableDeclaration<?>> getCallTargets(Resolvable<? extends ResolvedMethodLikeDeclaration> call) { return edgeSet().stream() .filter(e -> e.getCall() == call) .map(this::getEdgeTarget) .map(Vertex::getDeclaration) .map(CallableDeclaration.class::cast) .findFirst() .orElse(null); .map(decl -> (CallableDeclaration<?>) decl); } @Override Loading Loading @@ -105,8 +104,16 @@ public class CallGraph extends DirectedPseudograph<CallGraph.Vertex, CallGraph.E /** Find the calls to methods and constructors (edges) in the given list of compilation units. */ protected void buildEdges(NodeList<CompilationUnit> arg) { arg.accept(new VoidVisitorAdapter<Void>() { private final Deque<ClassOrInterfaceDeclaration> classStack = new LinkedList<>(); private final Deque<CallableDeclaration<?>> declStack = new LinkedList<>(); @Override public void visit(ClassOrInterfaceDeclaration n, Void arg) { classStack.push(n); super.visit(n, arg); classStack.pop(); } // ============ Method declarations =========== // There are some locations not considered, which may lead to an error in the stack. // 1. Method calls in non-static field initializations are assigned to all constructors of that class Loading @@ -129,24 +136,68 @@ public class CallGraph extends DirectedPseudograph<CallGraph.Vertex, CallGraph.E // =============== Method calls =============== @Override public void visit(MethodCallExpr n, Void arg) { n.resolve().toAst().ifPresent(decl -> addEdge(declStack.peek(), decl, n)); n.resolve().toAst().ifPresent(decl -> createPolyEdges(decl, n)); if (ASTUtils.shouldVisitArgumentsForMethodCalls(n)) super.visit(n, arg); } @Override public void visit(ObjectCreationExpr n, Void arg) { n.resolve().toAst().ifPresent(decl -> addEdge(declStack.peek(), decl, n)); n.resolve().toAst().ifPresent(decl -> createNormalEdge(decl, n)); if (ASTUtils.shouldVisitArgumentsForMethodCalls(n)) super.visit(n, arg); } @Override public void visit(ExplicitConstructorInvocationStmt n, Void arg) { n.resolve().toAst().ifPresent(decl -> addEdge(declStack.peek(), decl, n)); n.resolve().toAst().ifPresent(decl -> createNormalEdge(decl, n)); if (ASTUtils.shouldVisitArgumentsForMethodCalls(n)) super.visit(n, arg); } protected void createPolyEdges(MethodDeclaration decl, MethodCallExpr call) { // Static calls have no polymorphism, ignore if (decl.isStatic()) { createNormalEdge(decl, call); return; } Optional<Expression> scope = call.getScope(); // Determine the type of the call's scope Set<ClassOrInterfaceDeclaration> dynamicTypes; if (scope.isEmpty()) { // a) No scope: any class the method is in, or any outer class if the class is not static. // Early exit: it is easier to find the methods that override the // detected call than to account for all cases (implicit inner or outer class) classGraph.overriddenSetOf(decl) .forEach(methodDecl -> createNormalEdge(methodDecl, call)); return; } else if (scope.get().isThisExpr() && scope.get().asThisExpr().getTypeName().isEmpty()) { // b) just 'this', the current class and any subclass dynamicTypes = classGraph.subclassesOf(classStack.peek()); } else if (scope.get().isThisExpr()) { // c) 'ClassName.this', the given class and any subclass dynamicTypes = classGraph.subclassesOf(scope.get().asThisExpr().resolve().asClass()); } else { // d) others: compute possible dynamic types of the expression (TODO) dynamicTypes = classGraph.subclassesOf(scope.get().calculateResolvedType().asReferenceType()); } // Locate the corresponding methods for each possible dynamic type, they must be available to all // To locate them, use the method signature and search for it in the class graph // Connect to each declaration AtomicInteger edgesCreated = new AtomicInteger(); dynamicTypes.stream() .map(t -> classGraph.findMethodByTypeAndSignature(t, decl.getSignature())) .collect(Collectors.toCollection(NodeHashSet::new)) .forEach(methodDecl -> { edgesCreated.getAndIncrement(); createNormalEdge(methodDecl, call); }); assert edgesCreated.get() > 0; } protected void createNormalEdge(CallableDeclaration<?> decl, Resolvable<? extends ResolvedMethodLikeDeclaration> call) { addEdge(declStack.peek(), decl, call); } }, null); } Loading sdg-core/src/main/java/es/upv/mist/slicing/graphs/ClassGraph.java +97 −6 Original line number Diff line number Diff line Loading @@ -5,6 +5,7 @@ import com.github.javaparser.ast.NodeList; import com.github.javaparser.ast.body.*; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; import com.github.javaparser.resolution.declarations.ResolvedClassDeclaration; import com.github.javaparser.resolution.types.ResolvedReferenceType; import es.upv.mist.slicing.arcs.Arc; import es.upv.mist.slicing.utils.ASTUtils; import es.upv.mist.slicing.utils.Utils; Loading @@ -30,16 +31,75 @@ public class ClassGraph extends DirectedPseudograph<ClassGraph.Vertex, ClassGrap /** Locates the vertex that represents a given class or interface declaration. * The vertex must exist, or an exception will be thrown. */ protected Vertex findVertex(ResolvedClassDeclaration declaration) { Optional<Vertex> vertex = vertexSet().stream() .filter(v -> v.declaration instanceof ClassOrInterfaceDeclaration) protected Vertex findClassVertex(ClassOrInterfaceDeclaration declaration) { return vertexSet().stream() .filter(v -> v.declaration.isClassOrInterfaceDeclaration()) .filter(v -> ASTUtils.equalsWithRangeInCU(v.declaration, declaration)) .findFirst().orElseThrow(); } /** Locates the vertex that represents a given class or interface declaration. * The vertex must exist, or an exception will be thrown. */ protected Vertex findClassVertex(ResolvedClassDeclaration declaration) { return vertexSet().stream() .filter(v -> v.declaration.isClassOrInterfaceDeclaration()) .filter(v -> v.declaration.asClassOrInterfaceDeclaration().resolve().asClass().equals(declaration)) .findFirst(); return vertex.orElseThrow(); .findFirst().orElseThrow(); } protected Vertex findClassVertex(ResolvedReferenceType type) { return vertexSet().stream() .filter(v -> v.declaration.isClassOrInterfaceDeclaration()) .filter(v -> ASTUtils.resolvedTypeDeclarationToResolvedType(v.declaration.asClassOrInterfaceDeclaration().resolve()).equals(type)) .findFirst().orElseThrow(); } protected Vertex findMethodVertex(CallableDeclaration<?> declaration) { return vertexSet().stream() .filter(v -> v.declaration.isCallableDeclaration()) .filter(v -> ASTUtils.equalsWithRangeInCU(v.declaration, declaration)) .findFirst().orElseThrow(); } public Set<MethodDeclaration> overriddenSetOf(MethodDeclaration method) { return subclassesStreamOf(classVertexOf(findMethodVertex(method))) .flatMap(vertex -> outgoingEdgesOf(vertex).stream() .filter(ClassArc.Member.class::isInstance) .map(ClassGraph.this::getEdgeTarget) .filter(v -> v.declaration.isMethodDeclaration()) .filter(v -> v.declaration.asMethodDeclaration().getSignature().equals(method.getSignature())) .map(v -> v.declaration.asMethodDeclaration())) .collect(Collectors.toSet()); } protected Vertex classVertexOf(Vertex member) { assert member.declaration.isFieldDeclaration() || member.declaration.isCallableDeclaration() || member.declaration.isInitializerDeclaration(); return incomingEdgesOf(member).stream() .filter(ClassArc.Member.class::isInstance) .map(this::getEdgeSource) .findFirst().orElseThrow(); } /** Returns all child classes of the given class, including itself. */ public Set<ClassOrInterfaceDeclaration> subclassesOf(ClassOrInterfaceDeclaration clazz) { return subclassesOf(findClassVertex(clazz)); } /** Returns all child classes of the given class, including itself. */ public Set<ClassOrInterfaceDeclaration> subclassesOf(ResolvedClassDeclaration clazz) { return subclassesStreamOf(findVertex(clazz)) return subclassesOf(findClassVertex(clazz)); } public Set<ClassOrInterfaceDeclaration> subclassesOf(ResolvedReferenceType type) { return subclassesOf(findClassVertex(type)); } /** @see #subclassesOf(ClassOrInterfaceDeclaration) */ protected Set<ClassOrInterfaceDeclaration> subclassesOf(Vertex v) { return subclassesStreamOf(v) .map(Vertex::getDeclaration) .map(ClassOrInterfaceDeclaration.class::cast) .collect(Collectors.toSet()); } Loading @@ -51,6 +111,37 @@ public class ClassGraph extends DirectedPseudograph<ClassGraph.Vertex, ClassGrap .flatMap(this::subclassesStreamOf)); } // TODO: this method ignores default method implementations in interfaces, as can be overridden. /** Looks up a method in the graph, going up the class inheritance tree to locate a * matching method. If no match can be found, throws an {@link IllegalArgumentException}. */ public MethodDeclaration findMethodByTypeAndSignature(ClassOrInterfaceDeclaration type, CallableDeclaration.Signature signature) { Optional<MethodDeclaration> result = outgoingEdgesOf(findClassVertex(type)).stream() .filter(ClassArc.Member.class::isInstance) .map(this::getEdgeTarget) .map(Vertex::getDeclaration) .filter(BodyDeclaration::isMethodDeclaration) .map(BodyDeclaration::asMethodDeclaration) .filter(decl -> signature.equals(decl.getSignature())) .findFirst(); if (result.isPresent()) return result.get(); Optional<ClassOrInterfaceDeclaration> parentType = parentOf(type); if (parentType.isEmpty()) throw new IllegalArgumentException("Cannot find the given signature: " + signature); return findMethodByTypeAndSignature(parentType.get(), signature); } /** Find the parent class or interface of a given class. */ public Optional<ClassOrInterfaceDeclaration> parentOf(ClassOrInterfaceDeclaration declaration) { return incomingEdgesOf(findClassVertex(declaration)).stream() .filter(ClassArc.Extends.class::isInstance) .map(this::getEdgeSource) .map(Vertex::getDeclaration) .filter(BodyDeclaration::isClassOrInterfaceDeclaration) .map(BodyDeclaration::asClassOrInterfaceDeclaration) .findFirst(); } @Override public void build(NodeList<CompilationUnit> arg) { if (isBuilt()) Loading sdg-core/src/main/java/es/upv/mist/slicing/graphs/exceptionsensitive/ExceptionSensitiveCallConnector.java +6 −7 Original line number Diff line number Diff line Loading @@ -27,7 +27,8 @@ public class ExceptionSensitiveCallConnector extends CallConnector { @Override protected void connectCall(CallNode callNode, CallGraph callGraph) { var callExpr = callNode.getCallASTNode(); if (callGraph.getCallTarget(callExpr).getThrownExceptions().size() > 0) // We can pick any call, because the signatures must match if (callGraph.getCallTargets(callExpr).findFirst().orElseThrow().getThrownExceptions().size() > 0) handleExceptionReturnArcs(callExpr, callGraph); super.connectCall(callNode, callGraph); } Loading @@ -44,12 +45,10 @@ public class ExceptionSensitiveCallConnector extends CallConnector { .filter(SyntheticNode.class::isInstance) .map(n -> (SyntheticNode<?>) n) .collect(Collectors.toSet()); CallableDeclaration<?> decl = callGraph.getCallTarget(call); if (decl == null) throw new IllegalArgumentException("Unknown call!"); callGraph.getCallTargets(call).forEach(decl -> { connectNormalNodes(synthNodes, call, decl); connectExceptionNodes(synthNodes, call, decl); }); } /** Connects normal exit nodes to their corresponding return node. */ Loading sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/CallConnector.java +14 −13 Original line number Diff line number Diff line Loading @@ -14,8 +14,6 @@ import es.upv.mist.slicing.nodes.io.FormalIONode; import es.upv.mist.slicing.nodes.io.OutputNode; import es.upv.mist.slicing.utils.Logger; import java.util.Optional; /** Adds interprocedural arcs between the 'PDG components' of an SDG. * Arcs generated include {@link ParameterInOutArc parameter input/output} and * {@link CallArc call} arcs. */ Loading @@ -34,20 +32,23 @@ public class CallConnector { .forEach(node -> connectCall(node, callGraph)); } /** Connects a given call to its declaration, via call and in/out arcs. */ protected void connectCall(CallNode callNode, CallGraph callGraph) { /** Connects a given call to all possible matching declarations. */ @SuppressWarnings("unchecked") protected void connectCall(CallNode callNode, CallGraph callGraph) { var callExpr = (Resolvable<? extends ResolvedMethodLikeDeclaration>) callNode.getAstNode(); GraphNode<? extends CallableDeclaration<?>> declarationNode; try { declarationNode = Optional.ofNullable(callGraph.getCallTarget(callExpr)) .flatMap(sdg::findNodeByASTNode) .orElseThrow(IllegalArgumentException::new); } catch (IllegalArgumentException e) { callGraph.getCallTargets(callExpr) .map(sdg::findNodeByASTNode) .filter(opt -> { if (opt.isEmpty()) Logger.format("Method declaration not found: '%s'. Discarding", callExpr); return; return opt.isPresent(); }) .map(opt -> opt.orElseThrow(IllegalArgumentException::new)) .forEach(node -> connectCall(callNode, node)); } /** Connects a given call to its declaration, via call and in/out arcs. */ protected void connectCall(CallNode callNode, GraphNode<? extends CallableDeclaration<?>> declarationNode) { // Connect the call and declaration nodes sdg.addCallArc(callNode, declarationNode); Loading sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/SDG.java +3 −3 Original line number Diff line number Diff line Loading @@ -110,8 +110,8 @@ public class SDG extends Graph implements Sliceable, Buildable<NodeList<Compilat public void build(NodeList<CompilationUnit> nodeList) { // See creation strategy at http://kaz2.dsic.upv.es:3000/Fzg46cQvT1GzHQG9hFnP1g#Using-data-flow-in-the-SDG buildCFGs(nodeList); // 1 CallGraph callGraph = createCallGraph(nodeList); // 2 ClassGraph classGraph = createClassGraph(nodeList); // TODO: Update order and creation strategy CallGraph callGraph = createCallGraph(nodeList, classGraph); // 2 dataFlowAnalysis(callGraph); // 3 buildAndCopyPDGs(); // 4 connectCalls(callGraph); // 5 Loading @@ -138,8 +138,8 @@ public class SDG extends Graph implements Sliceable, Buildable<NodeList<Compilat } /** Create call graph from the list of compilation units. */ protected CallGraph createCallGraph(NodeList<CompilationUnit> nodeList) { CallGraph callGraph = new CallGraph(cfgMap); protected CallGraph createCallGraph(NodeList<CompilationUnit> nodeList, ClassGraph classGraph) { CallGraph callGraph = new CallGraph(cfgMap, classGraph); callGraph.build(nodeList); return callGraph; } Loading Loading
sdg-core/src/main/java/es/upv/mist/slicing/graphs/CallGraph.java +68 −17 Original line number Diff line number Diff line Loading @@ -2,10 +2,8 @@ package es.upv.mist.slicing.graphs; import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.NodeList; import com.github.javaparser.ast.body.CallableDeclaration; import com.github.javaparser.ast.body.ConstructorDeclaration; import com.github.javaparser.ast.body.InitializerDeclaration; import com.github.javaparser.ast.body.MethodDeclaration; import com.github.javaparser.ast.body.*; import com.github.javaparser.ast.expr.Expression; import com.github.javaparser.ast.expr.MethodCallExpr; import com.github.javaparser.ast.expr.ObjectCreationExpr; import com.github.javaparser.ast.stmt.ExplicitConstructorInvocationStmt; Loading @@ -15,16 +13,17 @@ import com.github.javaparser.resolution.declarations.ResolvedMethodLikeDeclarati import es.upv.mist.slicing.graphs.cfg.CFG; import es.upv.mist.slicing.nodes.GraphNode; import es.upv.mist.slicing.utils.ASTUtils; import es.upv.mist.slicing.utils.NodeHashSet; import es.upv.mist.slicing.utils.NodeNotFoundException; import es.upv.mist.slicing.utils.Utils; import org.jgrapht.graph.DefaultEdge; import org.jgrapht.graph.DirectedPseudograph; import org.jgrapht.nio.dot.DOTExporter; import java.util.Deque; import java.util.LinkedList; import java.util.Map; import java.util.Objects; import java.util.*; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; import java.util.stream.Stream; /** * A directed graph which displays the available method declarations as nodes and their Loading @@ -41,23 +40,23 @@ import java.util.Objects; public class CallGraph extends DirectedPseudograph<CallGraph.Vertex, CallGraph.Edge<?>> implements Buildable<NodeList<CompilationUnit>> { private final Map<CallableDeclaration<?>, CFG> cfgMap; private final Map<CallableDeclaration<?>, Vertex> vertexDeclarationMap = ASTUtils.newIdentityHashMap(); private final ClassGraph classGraph; private boolean built = false; public CallGraph(Map<CallableDeclaration<?>, CFG> cfgMap) { public CallGraph(Map<CallableDeclaration<?>, CFG> cfgMap, ClassGraph classGraph) { super(null, null, false); this.cfgMap = cfgMap; this.classGraph = classGraph; } /** Resolve a call to its declaration, by using the call AST nodes stored on the edges. */ public CallableDeclaration<?> getCallTarget(Resolvable<? extends ResolvedMethodLikeDeclaration> call) { /** Resolve a call to all its possible declarations, by using the call AST nodes stored on the edges. */ public Stream<CallableDeclaration<?>> getCallTargets(Resolvable<? extends ResolvedMethodLikeDeclaration> call) { return edgeSet().stream() .filter(e -> e.getCall() == call) .map(this::getEdgeTarget) .map(Vertex::getDeclaration) .map(CallableDeclaration.class::cast) .findFirst() .orElse(null); .map(decl -> (CallableDeclaration<?>) decl); } @Override Loading Loading @@ -105,8 +104,16 @@ public class CallGraph extends DirectedPseudograph<CallGraph.Vertex, CallGraph.E /** Find the calls to methods and constructors (edges) in the given list of compilation units. */ protected void buildEdges(NodeList<CompilationUnit> arg) { arg.accept(new VoidVisitorAdapter<Void>() { private final Deque<ClassOrInterfaceDeclaration> classStack = new LinkedList<>(); private final Deque<CallableDeclaration<?>> declStack = new LinkedList<>(); @Override public void visit(ClassOrInterfaceDeclaration n, Void arg) { classStack.push(n); super.visit(n, arg); classStack.pop(); } // ============ Method declarations =========== // There are some locations not considered, which may lead to an error in the stack. // 1. Method calls in non-static field initializations are assigned to all constructors of that class Loading @@ -129,24 +136,68 @@ public class CallGraph extends DirectedPseudograph<CallGraph.Vertex, CallGraph.E // =============== Method calls =============== @Override public void visit(MethodCallExpr n, Void arg) { n.resolve().toAst().ifPresent(decl -> addEdge(declStack.peek(), decl, n)); n.resolve().toAst().ifPresent(decl -> createPolyEdges(decl, n)); if (ASTUtils.shouldVisitArgumentsForMethodCalls(n)) super.visit(n, arg); } @Override public void visit(ObjectCreationExpr n, Void arg) { n.resolve().toAst().ifPresent(decl -> addEdge(declStack.peek(), decl, n)); n.resolve().toAst().ifPresent(decl -> createNormalEdge(decl, n)); if (ASTUtils.shouldVisitArgumentsForMethodCalls(n)) super.visit(n, arg); } @Override public void visit(ExplicitConstructorInvocationStmt n, Void arg) { n.resolve().toAst().ifPresent(decl -> addEdge(declStack.peek(), decl, n)); n.resolve().toAst().ifPresent(decl -> createNormalEdge(decl, n)); if (ASTUtils.shouldVisitArgumentsForMethodCalls(n)) super.visit(n, arg); } protected void createPolyEdges(MethodDeclaration decl, MethodCallExpr call) { // Static calls have no polymorphism, ignore if (decl.isStatic()) { createNormalEdge(decl, call); return; } Optional<Expression> scope = call.getScope(); // Determine the type of the call's scope Set<ClassOrInterfaceDeclaration> dynamicTypes; if (scope.isEmpty()) { // a) No scope: any class the method is in, or any outer class if the class is not static. // Early exit: it is easier to find the methods that override the // detected call than to account for all cases (implicit inner or outer class) classGraph.overriddenSetOf(decl) .forEach(methodDecl -> createNormalEdge(methodDecl, call)); return; } else if (scope.get().isThisExpr() && scope.get().asThisExpr().getTypeName().isEmpty()) { // b) just 'this', the current class and any subclass dynamicTypes = classGraph.subclassesOf(classStack.peek()); } else if (scope.get().isThisExpr()) { // c) 'ClassName.this', the given class and any subclass dynamicTypes = classGraph.subclassesOf(scope.get().asThisExpr().resolve().asClass()); } else { // d) others: compute possible dynamic types of the expression (TODO) dynamicTypes = classGraph.subclassesOf(scope.get().calculateResolvedType().asReferenceType()); } // Locate the corresponding methods for each possible dynamic type, they must be available to all // To locate them, use the method signature and search for it in the class graph // Connect to each declaration AtomicInteger edgesCreated = new AtomicInteger(); dynamicTypes.stream() .map(t -> classGraph.findMethodByTypeAndSignature(t, decl.getSignature())) .collect(Collectors.toCollection(NodeHashSet::new)) .forEach(methodDecl -> { edgesCreated.getAndIncrement(); createNormalEdge(methodDecl, call); }); assert edgesCreated.get() > 0; } protected void createNormalEdge(CallableDeclaration<?> decl, Resolvable<? extends ResolvedMethodLikeDeclaration> call) { addEdge(declStack.peek(), decl, call); } }, null); } Loading
sdg-core/src/main/java/es/upv/mist/slicing/graphs/ClassGraph.java +97 −6 Original line number Diff line number Diff line Loading @@ -5,6 +5,7 @@ import com.github.javaparser.ast.NodeList; import com.github.javaparser.ast.body.*; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; import com.github.javaparser.resolution.declarations.ResolvedClassDeclaration; import com.github.javaparser.resolution.types.ResolvedReferenceType; import es.upv.mist.slicing.arcs.Arc; import es.upv.mist.slicing.utils.ASTUtils; import es.upv.mist.slicing.utils.Utils; Loading @@ -30,16 +31,75 @@ public class ClassGraph extends DirectedPseudograph<ClassGraph.Vertex, ClassGrap /** Locates the vertex that represents a given class or interface declaration. * The vertex must exist, or an exception will be thrown. */ protected Vertex findVertex(ResolvedClassDeclaration declaration) { Optional<Vertex> vertex = vertexSet().stream() .filter(v -> v.declaration instanceof ClassOrInterfaceDeclaration) protected Vertex findClassVertex(ClassOrInterfaceDeclaration declaration) { return vertexSet().stream() .filter(v -> v.declaration.isClassOrInterfaceDeclaration()) .filter(v -> ASTUtils.equalsWithRangeInCU(v.declaration, declaration)) .findFirst().orElseThrow(); } /** Locates the vertex that represents a given class or interface declaration. * The vertex must exist, or an exception will be thrown. */ protected Vertex findClassVertex(ResolvedClassDeclaration declaration) { return vertexSet().stream() .filter(v -> v.declaration.isClassOrInterfaceDeclaration()) .filter(v -> v.declaration.asClassOrInterfaceDeclaration().resolve().asClass().equals(declaration)) .findFirst(); return vertex.orElseThrow(); .findFirst().orElseThrow(); } protected Vertex findClassVertex(ResolvedReferenceType type) { return vertexSet().stream() .filter(v -> v.declaration.isClassOrInterfaceDeclaration()) .filter(v -> ASTUtils.resolvedTypeDeclarationToResolvedType(v.declaration.asClassOrInterfaceDeclaration().resolve()).equals(type)) .findFirst().orElseThrow(); } protected Vertex findMethodVertex(CallableDeclaration<?> declaration) { return vertexSet().stream() .filter(v -> v.declaration.isCallableDeclaration()) .filter(v -> ASTUtils.equalsWithRangeInCU(v.declaration, declaration)) .findFirst().orElseThrow(); } public Set<MethodDeclaration> overriddenSetOf(MethodDeclaration method) { return subclassesStreamOf(classVertexOf(findMethodVertex(method))) .flatMap(vertex -> outgoingEdgesOf(vertex).stream() .filter(ClassArc.Member.class::isInstance) .map(ClassGraph.this::getEdgeTarget) .filter(v -> v.declaration.isMethodDeclaration()) .filter(v -> v.declaration.asMethodDeclaration().getSignature().equals(method.getSignature())) .map(v -> v.declaration.asMethodDeclaration())) .collect(Collectors.toSet()); } protected Vertex classVertexOf(Vertex member) { assert member.declaration.isFieldDeclaration() || member.declaration.isCallableDeclaration() || member.declaration.isInitializerDeclaration(); return incomingEdgesOf(member).stream() .filter(ClassArc.Member.class::isInstance) .map(this::getEdgeSource) .findFirst().orElseThrow(); } /** Returns all child classes of the given class, including itself. */ public Set<ClassOrInterfaceDeclaration> subclassesOf(ClassOrInterfaceDeclaration clazz) { return subclassesOf(findClassVertex(clazz)); } /** Returns all child classes of the given class, including itself. */ public Set<ClassOrInterfaceDeclaration> subclassesOf(ResolvedClassDeclaration clazz) { return subclassesStreamOf(findVertex(clazz)) return subclassesOf(findClassVertex(clazz)); } public Set<ClassOrInterfaceDeclaration> subclassesOf(ResolvedReferenceType type) { return subclassesOf(findClassVertex(type)); } /** @see #subclassesOf(ClassOrInterfaceDeclaration) */ protected Set<ClassOrInterfaceDeclaration> subclassesOf(Vertex v) { return subclassesStreamOf(v) .map(Vertex::getDeclaration) .map(ClassOrInterfaceDeclaration.class::cast) .collect(Collectors.toSet()); } Loading @@ -51,6 +111,37 @@ public class ClassGraph extends DirectedPseudograph<ClassGraph.Vertex, ClassGrap .flatMap(this::subclassesStreamOf)); } // TODO: this method ignores default method implementations in interfaces, as can be overridden. /** Looks up a method in the graph, going up the class inheritance tree to locate a * matching method. If no match can be found, throws an {@link IllegalArgumentException}. */ public MethodDeclaration findMethodByTypeAndSignature(ClassOrInterfaceDeclaration type, CallableDeclaration.Signature signature) { Optional<MethodDeclaration> result = outgoingEdgesOf(findClassVertex(type)).stream() .filter(ClassArc.Member.class::isInstance) .map(this::getEdgeTarget) .map(Vertex::getDeclaration) .filter(BodyDeclaration::isMethodDeclaration) .map(BodyDeclaration::asMethodDeclaration) .filter(decl -> signature.equals(decl.getSignature())) .findFirst(); if (result.isPresent()) return result.get(); Optional<ClassOrInterfaceDeclaration> parentType = parentOf(type); if (parentType.isEmpty()) throw new IllegalArgumentException("Cannot find the given signature: " + signature); return findMethodByTypeAndSignature(parentType.get(), signature); } /** Find the parent class or interface of a given class. */ public Optional<ClassOrInterfaceDeclaration> parentOf(ClassOrInterfaceDeclaration declaration) { return incomingEdgesOf(findClassVertex(declaration)).stream() .filter(ClassArc.Extends.class::isInstance) .map(this::getEdgeSource) .map(Vertex::getDeclaration) .filter(BodyDeclaration::isClassOrInterfaceDeclaration) .map(BodyDeclaration::asClassOrInterfaceDeclaration) .findFirst(); } @Override public void build(NodeList<CompilationUnit> arg) { if (isBuilt()) Loading
sdg-core/src/main/java/es/upv/mist/slicing/graphs/exceptionsensitive/ExceptionSensitiveCallConnector.java +6 −7 Original line number Diff line number Diff line Loading @@ -27,7 +27,8 @@ public class ExceptionSensitiveCallConnector extends CallConnector { @Override protected void connectCall(CallNode callNode, CallGraph callGraph) { var callExpr = callNode.getCallASTNode(); if (callGraph.getCallTarget(callExpr).getThrownExceptions().size() > 0) // We can pick any call, because the signatures must match if (callGraph.getCallTargets(callExpr).findFirst().orElseThrow().getThrownExceptions().size() > 0) handleExceptionReturnArcs(callExpr, callGraph); super.connectCall(callNode, callGraph); } Loading @@ -44,12 +45,10 @@ public class ExceptionSensitiveCallConnector extends CallConnector { .filter(SyntheticNode.class::isInstance) .map(n -> (SyntheticNode<?>) n) .collect(Collectors.toSet()); CallableDeclaration<?> decl = callGraph.getCallTarget(call); if (decl == null) throw new IllegalArgumentException("Unknown call!"); callGraph.getCallTargets(call).forEach(decl -> { connectNormalNodes(synthNodes, call, decl); connectExceptionNodes(synthNodes, call, decl); }); } /** Connects normal exit nodes to their corresponding return node. */ Loading
sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/CallConnector.java +14 −13 Original line number Diff line number Diff line Loading @@ -14,8 +14,6 @@ import es.upv.mist.slicing.nodes.io.FormalIONode; import es.upv.mist.slicing.nodes.io.OutputNode; import es.upv.mist.slicing.utils.Logger; import java.util.Optional; /** Adds interprocedural arcs between the 'PDG components' of an SDG. * Arcs generated include {@link ParameterInOutArc parameter input/output} and * {@link CallArc call} arcs. */ Loading @@ -34,20 +32,23 @@ public class CallConnector { .forEach(node -> connectCall(node, callGraph)); } /** Connects a given call to its declaration, via call and in/out arcs. */ protected void connectCall(CallNode callNode, CallGraph callGraph) { /** Connects a given call to all possible matching declarations. */ @SuppressWarnings("unchecked") protected void connectCall(CallNode callNode, CallGraph callGraph) { var callExpr = (Resolvable<? extends ResolvedMethodLikeDeclaration>) callNode.getAstNode(); GraphNode<? extends CallableDeclaration<?>> declarationNode; try { declarationNode = Optional.ofNullable(callGraph.getCallTarget(callExpr)) .flatMap(sdg::findNodeByASTNode) .orElseThrow(IllegalArgumentException::new); } catch (IllegalArgumentException e) { callGraph.getCallTargets(callExpr) .map(sdg::findNodeByASTNode) .filter(opt -> { if (opt.isEmpty()) Logger.format("Method declaration not found: '%s'. Discarding", callExpr); return; return opt.isPresent(); }) .map(opt -> opt.orElseThrow(IllegalArgumentException::new)) .forEach(node -> connectCall(callNode, node)); } /** Connects a given call to its declaration, via call and in/out arcs. */ protected void connectCall(CallNode callNode, GraphNode<? extends CallableDeclaration<?>> declarationNode) { // Connect the call and declaration nodes sdg.addCallArc(callNode, declarationNode); Loading
sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/SDG.java +3 −3 Original line number Diff line number Diff line Loading @@ -110,8 +110,8 @@ public class SDG extends Graph implements Sliceable, Buildable<NodeList<Compilat public void build(NodeList<CompilationUnit> nodeList) { // See creation strategy at http://kaz2.dsic.upv.es:3000/Fzg46cQvT1GzHQG9hFnP1g#Using-data-flow-in-the-SDG buildCFGs(nodeList); // 1 CallGraph callGraph = createCallGraph(nodeList); // 2 ClassGraph classGraph = createClassGraph(nodeList); // TODO: Update order and creation strategy CallGraph callGraph = createCallGraph(nodeList, classGraph); // 2 dataFlowAnalysis(callGraph); // 3 buildAndCopyPDGs(); // 4 connectCalls(callGraph); // 5 Loading @@ -138,8 +138,8 @@ public class SDG extends Graph implements Sliceable, Buildable<NodeList<Compilat } /** Create call graph from the list of compilation units. */ protected CallGraph createCallGraph(NodeList<CompilationUnit> nodeList) { CallGraph callGraph = new CallGraph(cfgMap); protected CallGraph createCallGraph(NodeList<CompilationUnit> nodeList, ClassGraph classGraph) { CallGraph callGraph = new CallGraph(cfgMap, classGraph); callGraph.build(nodeList); return callGraph; } Loading