From 73195590f01bf0175d50bf2aceaf237784d08bf1 Mon Sep 17 00:00:00 2001 From: Carlos Galindo Date: Wed, 20 Jan 2021 23:10:57 +0100 Subject: [PATCH 1/5] Polymorphic call graph. Naive version (no dynamic guessing) * Fix for SlicerTest: use StaticTypeSolver. * CallGraph#getCallTarget is now plural. * CallConnectors edited to iterate over targets. --- .../es/upv/mist/slicing/graphs/CallGraph.java | 83 +++++++++++--- .../upv/mist/slicing/graphs/ClassGraph.java | 103 +++++++++++++++++- .../ExceptionSensitiveCallConnector.java | 13 +-- .../slicing/graphs/sdg/CallConnector.java | 27 ++--- .../es/upv/mist/slicing/graphs/sdg/SDG.java | 6 +- .../java/es/upv/mist/slicing/SlicerTest.java | 9 +- 6 files changed, 188 insertions(+), 53 deletions(-) diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/CallGraph.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/CallGraph.java index ea738c4..d989bd3 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/CallGraph.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/CallGraph.java @@ -2,12 +2,11 @@ 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.expr.ThisExpr; import com.github.javaparser.ast.stmt.ExplicitConstructorInvocationStmt; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; import com.github.javaparser.resolution.Resolvable; @@ -21,10 +20,9 @@ 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.Stream; /** * A directed graph which displays the available method declarations as nodes and their @@ -41,23 +39,23 @@ import java.util.Objects; public class CallGraph extends DirectedPseudograph> implements Buildable> { private final Map, CFG> cfgMap; private final Map, Vertex> vertexDeclarationMap = ASTUtils.newIdentityHashMap(); + private final ClassGraph classGraph; private boolean built = false; - public CallGraph(Map, CFG> cfgMap) { + public CallGraph(Map, 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 call) { + /** Resolve a call to all its possible declarations, by using the call AST nodes stored on the edges. */ + public Stream> getCallTargets(Resolvable 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 @@ -105,8 +103,16 @@ public class CallGraph extends DirectedPseudograph arg) { arg.accept(new VoidVisitorAdapter() { + private final Deque classStack = new LinkedList<>(); private final Deque> 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 @@ -129,24 +135,67 @@ public class CallGraph extends DirectedPseudograph 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; + } + Expression scope = call.getScope().orElse(null); + // Determine the type of the call's scope + Set dynamicTypes; + // a) no scope/this: the current object or any subclass (we could be here but be a subtype) + if (scope == null || scope instanceof ThisExpr) { + if (scope == null) { + // 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.asThisExpr().getTypeName().isEmpty()) + dynamicTypes = classGraph.subclassesOf(classStack.peek()); + else + dynamicTypes = classGraph.subclassesOf(scope.asThisExpr().resolve().asClass()); + } else { // b) object/call/other: find possible dynamic types + dynamicTypes = classGraph.subclassesOf(scope.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())) + .filter(Optional::isPresent).map(Optional::get) + .distinct() + .forEach(methodDecl -> { + edgesCreated.getAndIncrement(); + createNormalEdge(methodDecl, call); + }); + assert edgesCreated.get() > 0; + } + + protected void createNormalEdge(CallableDeclaration decl, Resolvable call) { + addEdge(declStack.peek(), decl, call); + } }, null); } 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 index 899dfb4..2eaeaf1 100644 --- 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 @@ -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; @@ -30,16 +31,75 @@ public class ClassGraph extends DirectedPseudograph 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 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 subclassesOf(ClassOrInterfaceDeclaration clazz) { + return subclassesOf(findClassVertex(clazz)); + } + + /** Returns all child classes of the given class, including itself. */ public Set subclassesOf(ResolvedClassDeclaration clazz) { - return subclassesStreamOf(findVertex(clazz)) + return subclassesOf(findClassVertex(clazz)); + } + + public Set subclassesOf(ResolvedReferenceType type) { + return subclassesOf(findClassVertex(type)); + } + + /** @see #subclassesOf(ClassOrInterfaceDeclaration) */ + protected Set subclassesOf(Vertex v) { + return subclassesStreamOf(v) + .map(Vertex::getDeclaration) .map(ClassOrInterfaceDeclaration.class::cast) .collect(Collectors.toSet()); } @@ -51,6 +111,37 @@ public class ClassGraph extends DirectedPseudograph findMethodByTypeAndSignature(ClassOrInterfaceDeclaration type, CallableDeclaration.Signature signature) { + Optional 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; + Optional parentType = parentOf(type); + if (parentType.isEmpty()) + return Optional.empty(); + return findMethodByTypeAndSignature(parentType.get(), signature); + } + + /** Find the parent class or interface of a given class. */ + public Optional 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 arg) { if (isBuilt()) diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/exceptionsensitive/ExceptionSensitiveCallConnector.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/exceptionsensitive/ExceptionSensitiveCallConnector.java index 7c4f213..d43d57b 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/exceptionsensitive/ExceptionSensitiveCallConnector.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/exceptionsensitive/ExceptionSensitiveCallConnector.java @@ -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); } @@ -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!"); - - connectNormalNodes(synthNodes, call, decl); - connectExceptionNodes(synthNodes, call, decl); + callGraph.getCallTargets(call).forEach(decl -> { + connectNormalNodes(synthNodes, call, decl); + connectExceptionNodes(synthNodes, call, decl); + }); } /** Connects normal exit nodes to their corresponding return node. */ diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/CallConnector.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/CallConnector.java index b00417d..971f373 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/CallConnector.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/sdg/CallConnector.java @@ -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. */ @@ -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. */ + /** Connects a given call to all possible matching declarations. */ + @SuppressWarnings("unchecked") protected void connectCall(CallNode callNode, CallGraph callGraph) { - @SuppressWarnings("unchecked") var callExpr = (Resolvable) callNode.getAstNode(); - GraphNode> declarationNode; - try { - declarationNode = Optional.ofNullable(callGraph.getCallTarget(callExpr)) - .flatMap(sdg::findNodeByASTNode) - .orElseThrow(IllegalArgumentException::new); - } catch (IllegalArgumentException e) { - Logger.format("Method declaration not found: '%s'. Discarding", callExpr); - return; - } + callGraph.getCallTargets(callExpr) + .map(sdg::findNodeByASTNode) + .filter(opt -> { + if (opt.isEmpty()) + Logger.format("Method declaration not found: '%s'. Discarding", callExpr); + 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> declarationNode) { // Connect the call and declaration nodes sdg.addCallArc(callNode, declarationNode); 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 2f0cb03..35d6cee 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 @@ -110,8 +110,8 @@ public class SDG extends Graph implements Sliceable, Buildable 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 @@ -138,8 +138,8 @@ public class SDG extends Graph implements Sliceable, Buildable nodeList) { - CallGraph callGraph = new CallGraph(cfgMap); + protected CallGraph createCallGraph(NodeList nodeList, ClassGraph classGraph) { + CallGraph callGraph = new CallGraph(cfgMap, classGraph); callGraph.build(nodeList); return callGraph; } diff --git a/sdg-core/src/test/java/es/upv/mist/slicing/SlicerTest.java b/sdg-core/src/test/java/es/upv/mist/slicing/SlicerTest.java index 1d03298..21f0224 100644 --- a/sdg-core/src/test/java/es/upv/mist/slicing/SlicerTest.java +++ b/sdg-core/src/test/java/es/upv/mist/slicing/SlicerTest.java @@ -3,14 +3,12 @@ package es.upv.mist.slicing; import com.github.javaparser.StaticJavaParser; import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.NodeList; -import com.github.javaparser.symbolsolver.JavaSymbolSolver; -import com.github.javaparser.symbolsolver.resolution.typesolvers.CombinedTypeSolver; -import com.github.javaparser.symbolsolver.resolution.typesolvers.ReflectionTypeSolver; import es.upv.mist.slicing.graphs.exceptionsensitive.ESSDG; import es.upv.mist.slicing.graphs.sdg.SDG; import es.upv.mist.slicing.slicing.FileLineSlicingCriterion; import es.upv.mist.slicing.slicing.Slice; import es.upv.mist.slicing.slicing.SlicingCriterion; +import es.upv.mist.slicing.utils.StaticTypeSolver; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -25,10 +23,7 @@ import java.util.function.Supplier; public class SlicerTest { static { - StaticJavaParser.getConfiguration().setAttributeComments(false); - CombinedTypeSolver combinedTypeSolver = new CombinedTypeSolver(); - combinedTypeSolver.add(new ReflectionTypeSolver(true)); - StaticJavaParser.getConfiguration().setSymbolResolver(new JavaSymbolSolver(combinedTypeSolver)); + StaticTypeSolver.addTypeSolverJRE(); StaticJavaParser.getConfiguration().setAttributeComments(false); } -- GitLab From 9bc30e680975df96d2cdee3b70852b012179108b Mon Sep 17 00:00:00 2001 From: Carlos Galindo Date: Thu, 21 Jan 2021 00:33:16 +0100 Subject: [PATCH 2/5] fix! replace distinct with NodeHashSet --- .../src/main/java/es/upv/mist/slicing/graphs/CallGraph.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/CallGraph.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/CallGraph.java index d989bd3..f846098 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/CallGraph.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/CallGraph.java @@ -14,6 +14,7 @@ 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; @@ -22,6 +23,7 @@ import org.jgrapht.nio.dot.DOTExporter; import java.util.*; import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; import java.util.stream.Stream; /** @@ -185,7 +187,7 @@ public class CallGraph extends DirectedPseudograph classGraph.findMethodByTypeAndSignature(t, decl.getSignature())) .filter(Optional::isPresent).map(Optional::get) - .distinct() + .collect(Collectors.toCollection(NodeHashSet::new)) .forEach(methodDecl -> { edgesCreated.getAndIncrement(); createNormalEdge(methodDecl, call); -- GitLab From d118f4eddbb84e8a7f44cc82c0427d6a1f24e15a Mon Sep 17 00:00:00 2001 From: Carlos Galindo Date: Thu, 21 Jan 2021 00:40:19 +0100 Subject: [PATCH 3/5] fix! update tests (now polymorphic :D) --- .../test/res/regression/dinsa-tests/Josep2.java.sdg.sliced | 5 ++++- .../test/res/regression/review-07-2020/P5.java.sdg.sliced | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/sdg-core/src/test/res/regression/dinsa-tests/Josep2.java.sdg.sliced b/sdg-core/src/test/res/regression/dinsa-tests/Josep2.java.sdg.sliced index d9ca3aa..49c80ed 100644 --- a/sdg-core/src/test/res/regression/dinsa-tests/Josep2.java.sdg.sliced +++ b/sdg-core/src/test/res/regression/dinsa-tests/Josep2.java.sdg.sliced @@ -29,6 +29,9 @@ class Numeros { class GrandesNumeros extends Numeros { GrandesNumeros(double x) { - haceFalta = 0; + } + + int random() { + return haceFalta; } } diff --git a/sdg-core/src/test/res/regression/review-07-2020/P5.java.sdg.sliced b/sdg-core/src/test/res/regression/review-07-2020/P5.java.sdg.sliced index d9ca3aa..49c80ed 100644 --- a/sdg-core/src/test/res/regression/review-07-2020/P5.java.sdg.sliced +++ b/sdg-core/src/test/res/regression/review-07-2020/P5.java.sdg.sliced @@ -29,6 +29,9 @@ class Numeros { class GrandesNumeros extends Numeros { GrandesNumeros(double x) { - haceFalta = 0; + } + + int random() { + return haceFalta; } } -- GitLab From a1e87134dd088eb82797b26b30e74ab0a7d188e7 Mon Sep 17 00:00:00 2001 From: Carlos Galindo Date: Thu, 21 Jan 2021 22:44:49 +0100 Subject: [PATCH 4/5] fix! don't silently fail the method lookup --- .../src/main/java/es/upv/mist/slicing/graphs/CallGraph.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/CallGraph.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/CallGraph.java index f846098..0992c81 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/CallGraph.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/CallGraph.java @@ -186,7 +186,7 @@ public class CallGraph extends DirectedPseudograph classGraph.findMethodByTypeAndSignature(t, decl.getSignature())) - .filter(Optional::isPresent).map(Optional::get) + .map(Optional::orElseThrow) .collect(Collectors.toCollection(NodeHashSet::new)) .forEach(methodDecl -> { edgesCreated.getAndIncrement(); -- GitLab From 9a062531704456e4993caf0fa515040ad9933c63 Mon Sep 17 00:00:00 2001 From: Carlos Galindo Date: Thu, 21 Jan 2021 22:52:05 +0100 Subject: [PATCH 5/5] fix! error out when a method is not found reestructured if-else construct in #createPolyEdges() --- .../es/upv/mist/slicing/graphs/CallGraph.java | 34 +++++++++---------- .../upv/mist/slicing/graphs/ClassGraph.java | 6 ++-- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/CallGraph.java b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/CallGraph.java index 0992c81..6b1b527 100644 --- a/sdg-core/src/main/java/es/upv/mist/slicing/graphs/CallGraph.java +++ b/sdg-core/src/main/java/es/upv/mist/slicing/graphs/CallGraph.java @@ -6,7 +6,6 @@ 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.expr.ThisExpr; import com.github.javaparser.ast.stmt.ExplicitConstructorInvocationStmt; import com.github.javaparser.ast.visitor.VoidVisitorAdapter; import com.github.javaparser.resolution.Resolvable; @@ -162,23 +161,25 @@ public class CallGraph extends DirectedPseudograph scope = call.getScope(); // Determine the type of the call's scope Set dynamicTypes; - // a) no scope/this: the current object or any subclass (we could be here but be a subtype) - if (scope == null || scope instanceof ThisExpr) { - if (scope == null) { - // 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.asThisExpr().getTypeName().isEmpty()) - dynamicTypes = classGraph.subclassesOf(classStack.peek()); - else - dynamicTypes = classGraph.subclassesOf(scope.asThisExpr().resolve().asClass()); - } else { // b) object/call/other: find possible dynamic types - dynamicTypes = classGraph.subclassesOf(scope.calculateResolvedType().asReferenceType()); + 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 @@ -186,7 +187,6 @@ public class CallGraph extends DirectedPseudograph classGraph.findMethodByTypeAndSignature(t, decl.getSignature())) - .map(Optional::orElseThrow) .collect(Collectors.toCollection(NodeHashSet::new)) .forEach(methodDecl -> { edgesCreated.getAndIncrement(); 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 index 2eaeaf1..bd11813 100644 --- 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 @@ -114,7 +114,7 @@ public class ClassGraph extends DirectedPseudograph findMethodByTypeAndSignature(ClassOrInterfaceDeclaration type, CallableDeclaration.Signature signature) { + public MethodDeclaration findMethodByTypeAndSignature(ClassOrInterfaceDeclaration type, CallableDeclaration.Signature signature) { Optional result = outgoingEdgesOf(findClassVertex(type)).stream() .filter(ClassArc.Member.class::isInstance) .map(this::getEdgeTarget) @@ -124,10 +124,10 @@ public class ClassGraph extends DirectedPseudograph signature.equals(decl.getSignature())) .findFirst(); if (result.isPresent()) - return result; + return result.get(); Optional parentType = parentOf(type); if (parentType.isEmpty()) - return Optional.empty(); + throw new IllegalArgumentException("Cannot find the given signature: " + signature); return findMethodByTypeAndSignature(parentType.get(), signature); } -- GitLab