Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve CHA runtime and improve InvokeInterface resolving #734

Merged
merged 24 commits into from
Nov 20, 2023
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
4535a57
Changed the CHA resolving to safe time.
JonasKlauke Oct 30, 2023
6889f0b
removed unnecessary method.
JonasKlauke Oct 30, 2023
575a171
removed unnecessary method.
JonasKlauke Oct 30, 2023
32c22ac
Created Comparartor for Hierarchy with Tests
JonasKlauke Nov 13, 2023
9a03be0
fixed interface test that used wrong method names
JonasKlauke Nov 13, 2023
a400864
fmt
JonasKlauke Nov 13, 2023
7883bd3
improved CHA call graph algorithm
JonasKlauke Nov 13, 2023
a664493
removed compare call tests of two equal class types
JonasKlauke Nov 13, 2023
6772b96
fixed Typo
JonasKlauke Nov 14, 2023
a3fb26b
added concrete method resolving of classes with no implementation if …
JonasKlauke Nov 14, 2023
f374df3
Merge remote-tracking branch 'origin/develop' into improve/CallgraphR…
JonasKlauke Nov 14, 2023
a4db7b1
added missed method name refactoring after merge
JonasKlauke Nov 14, 2023
f79795e
shrank down unnecessary utility class MethodDispatchResolver. Removed…
JonasKlauke Nov 14, 2023
c066370
fixed broken field access to removed field
JonasKlauke Nov 14, 2023
231c8a0
reworked the RTA algorithm
JonasKlauke Nov 16, 2023
36a0da6
removed unnecessary utility method that is moved to the RTA call grap…
JonasKlauke Nov 16, 2023
d64a7be
ICFGDotExporter uses call graph for dispatches. removed unnecessary u…
JonasKlauke Nov 16, 2023
bbee224
Callgraph Algorithm stops at methods marked as Library. Added Test fo…
JonasKlauke Nov 16, 2023
c553668
improved library test
JonasKlauke Nov 16, 2023
4e83872
adapted tests to the ignored Library classes in the call graph algorithm
JonasKlauke Nov 17, 2023
3113dcf
removed warnings in testcases
JonasKlauke Nov 17, 2023
a75d56e
added testcase for incomplete concreteDispatch in which the concrete …
JonasKlauke Nov 17, 2023
7a8acf3
removed unnecessary method in AbstractCallGraphAlgorithm
JonasKlauke Nov 17, 2023
0ddd798
fmt
JonasKlauke Nov 17, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,7 +1,30 @@
package sootup.analysis.interprocedural.icfg;

/*-
* #%L
* Soot - a J*va Optimization Framework
* %%
* Copyright (C) 2022-2023 Palaniappan Muthuraman, Jonas Klauke
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 2.1 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Lesser Public License for more details.
*
* You should have received a copy of the GNU General Lesser Public
* License along with this program. If not, see
* <http://www.gnu.org/licenses/lgpl-2.1.html>.
* #L%
*/

import java.util.*;
import java.util.stream.Collectors;
import sootup.callgraph.CallGraph;
import sootup.core.graph.BasicBlock;
import sootup.core.graph.StmtGraph;
import sootup.core.jimple.common.expr.JNewExpr;
Expand All @@ -11,19 +34,20 @@
import sootup.core.model.SootMethod;
import sootup.core.signatures.MethodSignature;
import sootup.core.signatures.MethodSubSignature;
import sootup.core.typehierarchy.MethodDispatchResolver;
import sootup.core.types.VoidType;
import sootup.core.util.DotExporter;
import sootup.core.views.View;

public class ICFGDotExporter {

public static String buildICFGGraph(
Map<MethodSignature, StmtGraph> signatureToStmtGraph, View<? extends SootClass<?>> view) {
Map<MethodSignature, StmtGraph> signatureToStmtGraph,
View<? extends SootClass<?>> view,
CallGraph callGraph) {
final StringBuilder sb = new StringBuilder();
DotExporter.buildDiGraphObject(sb);
Map<Integer, MethodSignature> calls;
calls = computeCalls(signatureToStmtGraph, view);
calls = computeCalls(signatureToStmtGraph, view, callGraph);
for (Map.Entry<MethodSignature, StmtGraph> entry : signatureToStmtGraph.entrySet()) {
String graph = DotExporter.buildGraph(entry.getValue(), true, calls, entry.getKey());
sb.append(graph + "\n");
Expand All @@ -37,9 +61,13 @@ public static String buildICFGGraph(
* methods.
*/
public static Map<Integer, MethodSignature> computeCalls(
Map<MethodSignature, StmtGraph> stmtGraphSet, View<? extends SootClass<?>> view) {
Map<MethodSignature, StmtGraph> stmtGraphSet,
View<? extends SootClass<?>> view,
CallGraph callgraph) {
Map<Integer, MethodSignature> calls = new HashMap<>();
for (StmtGraph stmtGraph : stmtGraphSet.values()) {
for (Map.Entry<MethodSignature, StmtGraph> entry : stmtGraphSet.entrySet()) {
StmtGraph stmtGraph = entry.getValue();
MethodSignature source = entry.getKey();
Collection<? extends BasicBlock<?>> blocks;
try {
blocks = stmtGraph.getBlocksSorted();
Expand All @@ -50,11 +78,11 @@ public static Map<Integer, MethodSignature> computeCalls(
List<Stmt> stmts = block.getStmts();
for (Stmt stmt : stmts) {
if (stmt.containsInvokeExpr()) {
MethodSignature methodSignature = stmt.getInvokeExpr().getMethodSignature();
MethodSignature target = stmt.getInvokeExpr().getMethodSignature();
int hashCode = stmt.hashCode();
calls.put(hashCode, methodSignature);
calls.put(hashCode, target);
// compute all the classes that are made to the subclasses as well
connectEdgesToSubClasses(methodSignature, view, calls);
connectEdgesToSubClasses(source, target, view, calls, callgraph);
} else if (stmt instanceof JAssignStmt) {
JAssignStmt jAssignStmt = (JAssignStmt) stmt;
Integer currentHashCode = stmt.hashCode();
Expand Down Expand Up @@ -85,44 +113,40 @@ public static Map<Integer, MethodSignature> computeCalls(
}

public static Set<MethodSignature> getMethodSignatureInSubClass(
MethodSignature targetMethodSignature, View<? extends SootClass<?>> view) {
try {
return MethodDispatchResolver.resolveAllDispatches(view, targetMethodSignature).stream()
.map(
methodSignature ->
MethodDispatchResolver.resolveConcreteDispatch(view, methodSignature))
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toSet());
} catch (Exception e) {
return null;
MethodSignature source, MethodSignature target, CallGraph callGraph) {
if (!callGraph.containsMethod(source) || !callGraph.containsMethod(target)) {
return Collections.emptySet();
}
return callGraph.callsFrom(source).stream()
.filter(
methodSignature -> methodSignature.getSubSignature().equals(target.getSubSignature()))
.collect(Collectors.toSet());
}

public static void connectEdgesToSubClasses(
MethodSignature methodSignature,
MethodSignature source,
MethodSignature target,
View<? extends SootClass<?>> view,
Map<Integer, MethodSignature> calls) {
Map<Integer, MethodSignature> calls,
CallGraph callgraph) {
Set<MethodSignature> methodSignatureInSubClass =
getMethodSignatureInSubClass(methodSignature, view);
if (methodSignatureInSubClass != null) {
methodSignatureInSubClass.forEach(
subclassmethodSignature -> {
Optional<? extends SootMethod> method = view.getMethod(methodSignature);
MethodSignature initMethod =
new MethodSignature(
subclassmethodSignature.getDeclClassType(),
new MethodSubSignature(
"<init>", Collections.emptyList(), VoidType.getInstance()));
if (method.isPresent()
&& !subclassmethodSignature.toString().equals(initMethod.toString())) {
if (method.get().hasBody()) {
calls.put(
method.get().getBody().getStmtGraph().getStartingStmt().hashCode(),
subclassmethodSignature);
}
getMethodSignatureInSubClass(source, target, callgraph);
methodSignatureInSubClass.forEach(
subclassmethodSignature -> {
Optional<? extends SootMethod> method = view.getMethod(target);
MethodSignature initMethod =
new MethodSignature(
subclassmethodSignature.getDeclClassType(),
new MethodSubSignature(
"<init>", Collections.emptyList(), VoidType.getInstance()));
if (method.isPresent()
&& !subclassmethodSignature.toString().equals(initMethod.toString())) {
if (method.get().hasBody()) {
calls.put(
method.get().getBody().getStmtGraph().getStartingStmt().hashCode(),
subclassmethodSignature);
}
});
}
}
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -152,13 +152,23 @@ public JimpleBasedInterproceduralCFG(
public String buildICFGGraph(CallGraph callGraph) {
Map<MethodSignature, StmtGraph> signatureToStmtGraph = new LinkedHashMap<>();
computeAllCalls(mainMethodSignature, signatureToStmtGraph, callGraph);
return ICFGDotExporter.buildICFGGraph(signatureToStmtGraph, view);
return ICFGDotExporter.buildICFGGraph(signatureToStmtGraph, view, callGraph);
}

public void computeAllCalls(
MethodSignature methodSignature,
Map<MethodSignature, StmtGraph> signatureToStmtGraph,
CallGraph callGraph) {
ArrayList<MethodSignature> visitedMethods = new ArrayList<>();
computeAllCalls(methodSignature, signatureToStmtGraph, callGraph, visitedMethods);
}

private void computeAllCalls(
MethodSignature methodSignature,
Map<MethodSignature, StmtGraph> signatureToStmtGraph,
CallGraph callGraph,
List<MethodSignature> visitedMethods) {
visitedMethods.add(methodSignature);
final Optional<? extends SootMethod> methodOpt = view.getMethod(methodSignature);
// return if the methodSignature is already added to the hashMap to avoid stackoverflow error.
if (signatureToStmtGraph.containsKey(methodSignature)) return;
Expand All @@ -169,11 +179,12 @@ public void computeAllCalls(
signatureToStmtGraph.put(methodSignature, stmtGraph);
}
}
callGraph
.callsFrom(methodSignature)
callGraph.callsFrom(methodSignature).stream()
.filter(methodSignature1 -> !visitedMethods.contains(methodSignature1))
.forEach(
nextMethodSignature ->
computeAllCalls(nextMethodSignature, signatureToStmtGraph, callGraph));
computeAllCalls(
nextMethodSignature, signatureToStmtGraph, callGraph, visitedMethods));
}

private CallGraph initCallGraph() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ public String edgesFromCallGraph(
Map<MethodSignature, StmtGraph> signatureToStmtGraph = new LinkedHashMap<>();
icfg.computeAllCalls(methodSignature, signatureToStmtGraph, callGraph);
Map<Integer, MethodSignature> calls;
calls = ICFGDotExporter.computeCalls(signatureToStmtGraph, view);
calls = ICFGDotExporter.computeCalls(signatureToStmtGraph, view, callGraph);
final Optional<? extends SootMethod> methodOpt = view.getMethod(methodSignature);
if (methodOpt.isPresent()) {
SootMethod sootMethod = methodOpt.get();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import javax.annotation.Nonnull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sootup.core.IdentifierFactory;
import sootup.core.jimple.basic.Value;
import sootup.core.jimple.common.expr.AbstractInvokeExpr;
import sootup.core.jimple.common.expr.JStaticInvokeExpr;
Expand All @@ -40,6 +41,8 @@
import sootup.core.model.SootMethod;
import sootup.core.signatures.MethodSignature;
import sootup.core.signatures.MethodSubSignature;
import sootup.core.typehierarchy.HierarchyComparator;
import sootup.core.typehierarchy.TypeHierarchy;
import sootup.core.types.ClassType;
import sootup.core.views.View;
import sootup.java.core.JavaIdentifierFactory;
Expand Down Expand Up @@ -143,6 +146,11 @@
// skip if already processed
if (processed.contains(currentMethodSignature)) continue;

// skip if library class
SootClass<?> currentClass =
view.getClass(currentMethodSignature.getDeclClassType()).orElse(null);
if (currentClass == null || currentClass.isLibraryClass()) continue;

// perform pre-processing if needed
preProcessingMethod(view, currentMethodSignature, workList, cg);

Expand All @@ -151,9 +159,7 @@

// transform the method signature to the actual SootMethod
SootMethod currentMethod =
view.getClass(currentMethodSignature.getDeclClassType())
.flatMap(c -> c.getMethod(currentMethodSignature.getSubSignature()))
.orElse(null);
currentClass.getMethod(currentMethodSignature.getSubSignature()).orElse(null);

// get all call targets of invocations in the method body
Stream<MethodSignature> invocationTargets = resolveAllCallsFromSourceMethod(currentMethod);
Expand Down Expand Up @@ -458,4 +464,77 @@
@Nonnull
protected abstract Stream<MethodSignature> resolveCall(
SootMethod method, AbstractInvokeExpr invokeExpr);

/**
* Searches for the signature of the method that is the concrete implementation of <code>m</code>.
* This is done by checking each superclass and the class itself for whether it contains the
* concrete implementation.
*/
@Nonnull
public static Optional<MethodSignature> resolveConcreteDispatch(
View<? extends SootClass<?>> view, MethodSignature m) {
Optional<? extends SootMethod> methodOp = findConcreteMethod(view, m);
if (methodOp.isPresent()) {
SootMethod method = methodOp.get();
if (method.isAbstract()) {
return Optional.empty();
}
return Optional.of(method.getSignature());
}
return Optional.empty();

Check warning on line 484 in sootup.callgraph/src/main/java/sootup/callgraph/AbstractCallGraphAlgorithm.java

View check run for this annotation

Codecov / codecov/patch

sootup.callgraph/src/main/java/sootup/callgraph/AbstractCallGraphAlgorithm.java#L484

Added line #L484 was not covered by tests
}

/**
* searches the method object in the given hierarchy
*
* @param view it contains all classes
* @param sig the signature of the searched method
* @return the found method object, or null if the method was not found.
*/
public static Optional<? extends SootMethod> findConcreteMethod(
@Nonnull View<? extends SootClass<?>> view, @Nonnull MethodSignature sig) {
IdentifierFactory identifierFactory = view.getIdentifierFactory();
Optional<? extends SootMethod> startMethod = view.getMethod(sig);
if (startMethod.isPresent()) {
return startMethod;
}
TypeHierarchy typeHierarchy = view.getTypeHierarchy();

List<ClassType> superClasses = typeHierarchy.superClassesOf(sig.getDeclClassType());
for (ClassType superClassType : superClasses) {
Optional<? extends SootMethod> method =
view.getMethod(
identifierFactory.getMethodSignature(superClassType, sig.getSubSignature()));
if (method.isPresent()) {
return method;
}
}
Set<ClassType> interfaces = typeHierarchy.implementedInterfacesOf(sig.getDeclClassType());
// interface1 is a sub-interface of interface2
// interface1 is a super-interface of interface2
// due to multiple inheritance in interfaces
final HierarchyComparator hierarchyComparator = new HierarchyComparator(view);
Optional<? extends SootMethod> defaultMethod =
interfaces.stream()
.map(
classType ->
view.getMethod(
identifierFactory.getMethodSignature(classType, sig.getSubSignature())))
.filter(Optional::isPresent)
.map(Optional::get)
.min(
(m1, m2) ->
hierarchyComparator.compare(
m1.getDeclaringClassType(), m2.getDeclaringClassType()));
if (defaultMethod.isPresent()) {
return defaultMethod;
}
logger.warn(

Check warning on line 532 in sootup.callgraph/src/main/java/sootup/callgraph/AbstractCallGraphAlgorithm.java

View check run for this annotation

Codecov / codecov/patch

sootup.callgraph/src/main/java/sootup/callgraph/AbstractCallGraphAlgorithm.java#L532

Added line #L532 was not covered by tests
"Could not find \""
+ sig.getSubSignature()

Check warning on line 534 in sootup.callgraph/src/main/java/sootup/callgraph/AbstractCallGraphAlgorithm.java

View check run for this annotation

Codecov / codecov/patch

sootup.callgraph/src/main/java/sootup/callgraph/AbstractCallGraphAlgorithm.java#L534

Added line #L534 was not covered by tests
+ "\" in "
+ sig.getDeclClassType().getClassName()

Check warning on line 536 in sootup.callgraph/src/main/java/sootup/callgraph/AbstractCallGraphAlgorithm.java

View check run for this annotation

Codecov / codecov/patch

sootup.callgraph/src/main/java/sootup/callgraph/AbstractCallGraphAlgorithm.java#L536

Added line #L536 was not covered by tests
+ " and in its superclasses and interfaces");
return Optional.empty();

Check warning on line 538 in sootup.callgraph/src/main/java/sootup/callgraph/AbstractCallGraphAlgorithm.java

View check run for this annotation

Codecov / codecov/patch

sootup.callgraph/src/main/java/sootup/callgraph/AbstractCallGraphAlgorithm.java#L538

Added line #L538 was not covered by tests
}
}
Loading