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 all 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 @@ -276,47 +282,6 @@
.map(SootClassMember::getSignature);
}

/**
* searches the method object in the given hierarchy
*
* @param view it contains all classes
* @param sig the signature of the searched method
* @param <T> the generic type of the searched method object
* @return the found method object, or null if the method was not found.
*/
protected final <T extends Method> T findMethodInHierarchy(
@Nonnull View<? extends SootClass<?>> view, @Nonnull MethodSignature sig) {
Optional<? extends SootClass<?>> optSc = view.getClass(sig.getDeclClassType());

if (optSc.isPresent()) {
SootClass<?> sc = optSc.get();

List<ClassType> superClasses = view.getTypeHierarchy().superClassesOf(sc.getType());
Set<ClassType> interfaces = view.getTypeHierarchy().implementedInterfacesOf(sc.getType());
superClasses.addAll(interfaces);

for (ClassType superClassType : superClasses) {
Optional<? extends SootClass<?>> superClassOpt = view.getClass(superClassType);
if (superClassOpt.isPresent()) {
SootClass<?> superClass = superClassOpt.get();
Optional<? extends SootMethod> methodOpt = superClass.getMethod(sig.getSubSignature());
if (methodOpt.isPresent()) {
return (T) methodOpt.get();
}
}
}
logger.warn(
"Could not find \""
+ sig.getSubSignature()
+ "\" in "
+ sig.getDeclClassType().getClassName()
+ " and in its superclasses");
} else {
logger.trace("Could not find \"" + sig.getDeclClassType() + "\" in view");
}
return null;
}

/**
* This method enables optional pre-processing of a method in the call graph algorithm
*
Expand Down Expand Up @@ -438,7 +403,7 @@
"There are more than 1 main method present.\n Below main methods are found: \n"
+ mainMethods
+ "\n initialize() method can be used if only one main method exists. \n You can specify these main methods as entry points by passing them as parameter to initialize method.");
} else if (mainMethods.size() == 0) {
} else if (mainMethods.isEmpty()) {
throw new RuntimeException(
"No main method is present in the input programs. initialize() method can be used if only one main method exists in the input program and that should be used as entry point for call graph. \n Please specify entry point as a parameter to initialize method.");
}
Expand All @@ -458,4 +423,87 @@
@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();
}

/**
* 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();
SootClass<?> startclass = view.getClass(sig.getDeclClassType()).orElse(null);
if (startclass == null) {
logger.warn(

Check warning on line 458 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#L458

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

Check warning on line 460 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#L460

Added line #L460 was not covered by tests
+ "\" of method"
+ sig
+ " to resolve the concrete method");
return Optional.empty();

Check warning on line 464 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#L464

Added line #L464 was not covered by tests
}
Optional<? extends SootMethod> startMethod = startclass.getMethod(sig.getSubSignature());
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(
"Could not find \""
+ sig.getSubSignature()
+ "\" in "
+ sig.getDeclClassType().getClassName()
+ " and in its superclasses and interfaces");
return Optional.empty();
}
}
Loading