Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
Expand Up @@ -28,6 +28,7 @@
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.jspecify.annotations.NonNull;
import org.jspecify.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sootup.callgraph.CallGraph.Call;
Expand Down Expand Up @@ -81,6 +82,19 @@ protected boolean includeCall(@NonNull SootMethod method, @NonNull InvokableStmt
return true;
}

/**
* Decide whether a call from <code>sourceMethod</code> to the <code>targetMethod</code> shall be
* added to the call graph. Default: accept everything. Subclasses can override this method to
* implement pruning.
*
* @param sourceMethod the source (caller) method
* @param targetMethod the target method of the call
*/
protected boolean includeCallToTarget(
@NonNull MethodSignature sourceMethod, @NonNull MethodSignature targetMethod) {
return true;
}

/**
* This method starts the construction of the call graph algorithm. It initializes the needed
* objects for the call graph generation and calls processWorkList method.
Expand All @@ -91,6 +105,24 @@ protected boolean includeCall(@NonNull SootMethod method, @NonNull InvokableStmt
*/
@NonNull
final CallGraph constructCompleteCallGraph(List<MethodSignature> entryPoints) {
return constructCompleteCallGraph(entryPoints, null);
}

/**
* This method starts the construction of the call graph algorithm. It initializes the needed
* objects for the call graph generation and calls processWorkList method.
*
* @param entryPoints a list of method signatures that will be added to the work list in the call
* graph generation.
* @param basePackageName the base package name of the source application. This is used to filter
* out all classes from the call graph that do not contain this package name.
* @return the complete constructed call graph starting from the entry methods.
*/
@NonNull
final CallGraph constructCompleteCallGraph(
List<MethodSignature> entryPoints, @Nullable String basePackageName) {
basePackageName = basePackageName != null ? basePackageName.toLowerCase() : null;

Deque<MethodSignature> workList = new ArrayDeque<>(entryPoints);
Set<MethodSignature> processed = new HashSet<>();

Expand All @@ -100,7 +132,7 @@ final CallGraph constructCompleteCallGraph(List<MethodSignature> entryPoints) {
workList.addAll(clinits);
MutableCallGraph cg = initializeCallGraph(entryPoints, clinits);

processWorkList(workList, processed, cg);
processWorkList(workList, processed, cg, basePackageName);
return cg;
}

Expand Down Expand Up @@ -149,9 +181,14 @@ private Optional<MethodSignature> getSignatureOfImplementedStaticInitializer(
* This list is filled in the execution with found call targets in the call graph algorithm.
* @param processed the list of processed method to only process the method once.
* @param cg the call graph object that is filled with the found methods and call edges.
* @param basePackageName the base package name of the source application. This is used to filter
* out all classes from the call graph that do not contain this package name.
*/
final void processWorkList(
Deque<MethodSignature> workList, Set<MethodSignature> processed, MutableCallGraph cg) {
Deque<MethodSignature> workList,
Set<MethodSignature> processed,
MutableCallGraph cg,
@Nullable String basePackageName) {
while (!workList.isEmpty()) {
MethodSignature currentMethodSignature = workList.pop();
// skip if already processed
Expand All @@ -162,7 +199,10 @@ final void processWorkList(
// skip if library class
SootClass currentClass =
view.getClass(currentMethodSignature.getDeclClassType()).orElse(null);
if (currentClass == null || currentClass.isLibraryClass()) {
if (currentClass == null
|| currentClass.isLibraryClass()
|| (basePackageName != null
&& !isClassPartOfApplication(currentClass, basePackageName))) {
continue;
}

Expand All @@ -186,7 +226,8 @@ final void processWorkList(
resolveAllCallsFromSourceMethod(currentMethod, cg, workList);

// get all call targets of implicit edges in the method body
resolveAllImplicitCallsFromSourceMethod(currentMethod, cg, workList);
resolveAllImplicitCallsFromSourceMethod(
currentMethod, cg, workList, basePackageName);
});

// set method as processed
Expand All @@ -197,6 +238,17 @@ final void processWorkList(
}
}

private boolean isClassPartOfApplication(SootClass sootClass, String basePackageName) {
return isClassPartOfApplication(sootClass.getClassSource().getClassType(), basePackageName);
}

private boolean isClassPartOfApplication(ClassType classType, String basePackageName) {
if (basePackageName == null) {
return true;
}
return classType.getPackageName().toString().toLowerCase().contains(basePackageName);
}

/**
* Adds the defined call to the given call graph. If the source or target method was added as
* vertex to the call graph, they will be added to the worklist
Expand All @@ -214,6 +266,10 @@ protected void addCallToCG(
@NonNull InvokableStmt invokeStmt,
@NonNull MutableCallGraph cg,
@NonNull Deque<MethodSignature> workList) {
if (!includeCallToTarget(source, target)) {
return;
}

if (!cg.containsMethod(source)) {
cg.addMethod(source);
workList.push(source);
Expand Down Expand Up @@ -337,14 +393,17 @@ protected void implicitStartRunCall(
* @param sourceMethod the inspected source method
* @param cg new calls will be added to the call graph
* @param workList new target methods will be added to the work list
* @param basePackageName the base package name of the source application. This is used to filter
* out all classes from the call graph that do not contain this package name.
*/
protected void resolveAllImplicitCallsFromSourceMethod(
@NonNull SootMethod sourceMethod,
@NonNull MutableCallGraph cg,
@NonNull Deque<MethodSignature> workList) {
@NonNull Deque<MethodSignature> workList,
@Nullable String basePackageName) {
implicitStartRunCall(sourceMethod, cg, workList);
// collect all static initializer calls
resolveAllStaticInitializerCalls(sourceMethod, cg, workList);
resolveAllStaticInitializerCalls(sourceMethod, cg, workList, basePackageName);
}

/**
Expand All @@ -353,11 +412,14 @@ protected void resolveAllImplicitCallsFromSourceMethod(
* @param sourceMethod the inspected source method
* @param cg clinit calls will be added to the call graph
* @param workList found clinit methods will be added to the work list
* @param basePackageName the base package name of the source application. This is used to filter
* out all classes from the call graph that do not contain this package name.
*/
protected void resolveAllStaticInitializerCalls(
@NonNull SootMethod sourceMethod,
@NonNull MutableCallGraph cg,
@NonNull Deque<MethodSignature> workList) {
@NonNull Deque<MethodSignature> workList,
@Nullable String basePackageName) {
MethodSignature sourceMethodSignature = sourceMethod.getSignature();

InstantiateClassValueVisitor instantiateVisitor = new InstantiateClassValueVisitor();
Expand All @@ -371,12 +433,19 @@ protected void resolveAllStaticInitializerCalls(
if (invokableStmt.containsFieldRef()
&& invokableStmt.getFieldRef() instanceof JStaticFieldRef) {
targetClass = invokableStmt.getFieldRef().getFieldSignature().getDeclClassType();
if (!(targetClass
.getFullyQualifiedName()
.equals(sourceMethodSignature.getDeclClassType().getFullyQualifiedName())
&& sourceMethodSignature.getName().equals("<clinit>"))) {
if (isClassPartOfApplication(targetClass, basePackageName)
&& !(targetClass
.getFullyQualifiedName()
.equals(
sourceMethodSignature.getDeclClassType().getFullyQualifiedName())
&& sourceMethodSignature.getName().equals("<clinit>"))) {
addStaticInitializerCalls(
sourceMethodSignature, targetClass, invokableStmt, cg, workList);
sourceMethodSignature,
targetClass,
invokableStmt,
cg,
workList,
basePackageName);
}
}
// static method
Expand All @@ -387,13 +456,21 @@ protected void resolveAllStaticInitializerCalls(
ClassType newTargetClass = expr.getMethodSignature().getDeclClassType();
// checks if the field points to the same clinit
if (!newTargetClass.equals(targetClass)) {
if (!(newTargetClass
.getFullyQualifiedName()
.equals(
sourceMethodSignature.getDeclClassType().getFullyQualifiedName())
&& sourceMethodSignature.getName().equals("<clinit>"))) {
if (isClassPartOfApplication(newTargetClass, basePackageName)
&& !(newTargetClass
.getFullyQualifiedName()
.equals(
sourceMethodSignature
.getDeclClassType()
.getFullyQualifiedName())
&& sourceMethodSignature.getName().equals("<clinit>"))) {
addStaticInitializerCalls(
sourceMethodSignature, newTargetClass, invokableStmt, cg, workList);
sourceMethodSignature,
newTargetClass,
invokableStmt,
cg,
workList,
basePackageName);
}
}
}
Expand All @@ -406,13 +483,21 @@ protected void resolveAllStaticInitializerCalls(
ClassType newTargetClass = instantiateVisitor.getResult();
// check if class type is the same as in the field which could be on the left op
if (newTargetClass != null && !newTargetClass.equals(targetClass)) {
if (!(newTargetClass
.getFullyQualifiedName()
.equals(
sourceMethodSignature.getDeclClassType().getFullyQualifiedName())
&& sourceMethodSignature.getName().equals("<clinit>"))) {
if (isClassPartOfApplication(newTargetClass, basePackageName)
&& !(newTargetClass
.getFullyQualifiedName()
.equals(
sourceMethodSignature
.getDeclClassType()
.getFullyQualifiedName())
&& sourceMethodSignature.getName().equals("<clinit>"))) {
addStaticInitializerCalls(
sourceMethodSignature, newTargetClass, invokableStmt, cg, workList);
sourceMethodSignature,
newTargetClass,
invokableStmt,
cg,
workList,
basePackageName);
}
}
}
Expand All @@ -430,13 +515,16 @@ protected void resolveAllStaticInitializerCalls(
* @param invokableStmt the statement causing the call
* @param cg the call graph that will contain the found calls
* @param workList the work list that will be updated with new target methods
* @param basePackageName the base package name of the source application. This is used to filter
* out all classes from the call graph that do not contain this package name.
*/
private void addStaticInitializerCalls(
MethodSignature sourceSig,
ClassType targetClass,
InvokableStmt invokableStmt,
MutableCallGraph cg,
Deque<MethodSignature> workList) {
Deque<MethodSignature> workList,
String basePackageName) {
// static initializer call of class
view.getMethod(view.getIdentifierFactory().getStaticInitializerSignature(targetClass))
.ifPresent(
Expand All @@ -445,6 +533,7 @@ private void addStaticInitializerCalls(
// static initializer calls of all superclasses
typeHierarchy
.superClassesOf(targetClass)
.filter(classType -> isClassPartOfApplication(classType, basePackageName))
.map(
classType ->
view.getMethod(
Expand Down Expand Up @@ -501,7 +590,7 @@ public CallGraph addClass(@NonNull CallGraph oldCallGraph, @NonNull ClassType cl
// Step 1: Add edges from the new methods to other methods
Deque<MethodSignature> workList = new ArrayDeque<>(newMethodSignatures);
Set<MethodSignature> processed = new HashSet<>(oldCallGraph.getMethodSignatures());
processWorkList(workList, processed, updated);
processWorkList(workList, processed, updated, null);

// Step 2: Add edges from old methods to methods overridden in the new class
Stream<ClassType> superClasses = typeHierarchy.superClassesOf(classType);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

import java.util.List;
import org.jspecify.annotations.NonNull;
import org.jspecify.annotations.Nullable;
import sootup.core.signatures.MethodSignature;
import sootup.core.types.ClassType;

Expand All @@ -48,6 +49,19 @@ public interface CallGraphAlgorithm {
*/
@NonNull CallGraph initialize(@NonNull List<MethodSignature> entryPoints);

/**
* This method initializes and starts the call graph algorithm with given entry points. The entry
* points define the start methods in the call graph algorithm.
*
* @param entryPoints a list of entry points for the call graph algorithm. The algorithm starts at
* these methods and inspects all reachable methods.
* @param basePackageName the base package name of the source application. This is used to filter
* out all classes from the call graph that do not contain this package name.
* @return a generated call graph with every entry point as starting point.
*/
@NonNull CallGraph initialize(
@NonNull List<MethodSignature> entryPoints, @Nullable String basePackageName);

/**
* Adds a class to the call graph. All methods will be set as entry points in the call graph
* algorithm. Starts the call graph algorithm. The found edges will be added to the call graph.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.jspecify.annotations.NonNull;
import org.jspecify.annotations.Nullable;
import sootup.core.jimple.common.expr.AbstractInvokeExpr;
import sootup.core.jimple.common.expr.JDynamicInvokeExpr;
import sootup.core.jimple.common.expr.JSpecialInvokeExpr;
Expand Down Expand Up @@ -57,13 +58,19 @@ public ClassHierarchyAnalysisAlgorithm(@NonNull View view) {
@NonNull
@Override
public CallGraph initialize() {
return constructCompleteCallGraph(Collections.singletonList(findMainMethod()));
return constructCompleteCallGraph(Collections.singletonList(findMainMethod()), null);
}

@NonNull
@Override
public CallGraph initialize(@NonNull List<MethodSignature> entryPoints) {
return constructCompleteCallGraph(entryPoints);
return constructCompleteCallGraph(entryPoints, null);
}

@Override
public @NonNull CallGraph initialize(
@NonNull List<MethodSignature> entryPoints, @Nullable String basePackageName) {
return constructCompleteCallGraph(entryPoints, basePackageName);
}

/**
Expand All @@ -89,6 +96,10 @@ protected Stream<MethodSignature> resolveCall(SootMethod method, InvokableStmt i
return Stream.empty();
}

if (!includeCallToTarget(method.getSignature(), targetMethodSignature)) {
return Stream.empty();
}

SootMethod actualTargetMethod = view.getMethod(targetMethodSignature).orElse(null);
if (actualTargetMethod == null) {
// method not implemented, search for implementation in super classes or ínterfaces
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import java.util.*;
import java.util.stream.Stream;
import org.jspecify.annotations.NonNull;
import org.jspecify.annotations.Nullable;
import sootup.callgraph.CallGraph.Call;
import sootup.core.jimple.common.expr.AbstractInvokeExpr;
import sootup.core.jimple.common.expr.JDynamicInvokeExpr;
Expand Down Expand Up @@ -83,14 +84,20 @@ public CallGraph initialize() {
return initialize(entryPoints);
}

@Override
public @NonNull CallGraph initialize(@NonNull List<MethodSignature> entryPoints) {
return initialize(entryPoints, null);
}

@NonNull
@Override
public CallGraph initialize(@NonNull List<MethodSignature> entryPoints) {
public CallGraph initialize(
@NonNull List<MethodSignature> entryPoints, @Nullable String basePackageName) {
// init helper data structures
instantiatedClasses = new HashSet<>(instantiatedClasses);
ignoredCalls = ArrayListMultimap.create();

CallGraph cg = constructCompleteCallGraph(entryPoints);
CallGraph cg = constructCompleteCallGraph(entryPoints, basePackageName);

// delete the data structures
instantiatedClasses = Collections.emptySet();
Expand Down
Loading