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

issue 965 implement call graph diff #976

Merged
merged 13 commits into from
Jul 19, 2024
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*.class
!shared-test-resources/miniTestSuite/**/*.class
!sootup.analysis/src/test/resources/taint/binary/*.class
!sootup.callgraph/src/test/resources/callgraph/CallGraphDifference/binary/*.class

# Log file
*.log
Expand Down
35 changes: 35 additions & 0 deletions shared-test-resources/CallGraphDifference/Example.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
class Example {

public static void main(String[] args) {
A objB = new B();
B.staticDispatch(new C());

A objC=new E();

objB.virtualDispatch();
}
}

class A extends Object {
public void virtualDispatch() { }
public static void staticDispatch( Object o) { }
}

class B extends A {
public void virtualDispatch() { }
public static void staticDispatch( Object o) { }
}

class C extends D {
public static void staticDispatch( Object o) { }
}

class D extends A {
public void virtualDispatch() { }
public static void staticDispatch( Object o) { }
}

class E extends A {
public void virtualDispatch() { }
public static void staticDispatch( Object o) { }
}
Original file line number Diff line number Diff line change
Expand Up @@ -96,4 +96,11 @@ boolean containsCall(
*/
@Nonnull
MutableCallGraph copy();

/**
* This method compares the difference between the current call graph and call graph passed into
* the argument.
*/
@Nonnull
CallGraphDifference diff(@Nonnull CallGraph callGraph);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
package sootup.callgraph;

import java.util.*;
import java.util.stream.Collectors;
import org.apache.commons.lang3.tuple.MutablePair;
import org.apache.commons.lang3.tuple.Pair;
import sootup.core.signatures.MethodSignature;

/*-
* #%L
* Soot
* %%
* Copyright (C) 2024 Sahil Agichani
* %%
* 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%
*/

public class CallGraphDifference {

private final CallGraph baseCallGraph;
private final CallGraph otherCallGraph;

private final List<Pair<MethodSignature, MethodSignature>> baseCallGraphEdges;
private final List<Pair<MethodSignature, MethodSignature>> otherCallGraphEdges;

public CallGraphDifference(CallGraph baseCallGraph, CallGraph otherCallGraph) {
this.baseCallGraph = baseCallGraph;
this.otherCallGraph = otherCallGraph;
this.baseCallGraphEdges = constructEdges(baseCallGraph);
this.otherCallGraphEdges = constructEdges(otherCallGraph);
}

private List<Pair<MethodSignature, MethodSignature>> constructEdges(CallGraph cg) {
List<Pair<MethodSignature, MethodSignature>> cgEdges = new ArrayList<>();
for (MethodSignature srcNode : cg.getMethodSignatures()) {
Set<MethodSignature> outNodes = cg.callsFrom(srcNode);
for (MethodSignature targetNode : outNodes) {
cgEdges.add(new MutablePair<>(srcNode, targetNode));
}
}
return cgEdges;
}

public CallGraph getBaseCallGraph() {
return baseCallGraph;
}

public CallGraph getOtherCallGraph() {
return otherCallGraph;
}

public List<Pair<MethodSignature, MethodSignature>> getBaseCallGraphEdges() {
return baseCallGraphEdges;
}

public List<Pair<MethodSignature, MethodSignature>> getOtherCallGraphEdges() {
return otherCallGraphEdges;
}

/*
In the intersectedCalls() function, we iterate over each edge in both call graphs and
return the intersection of the edges.
*/
public List<Pair<MethodSignature, MethodSignature>> intersectedCalls() {
return baseCallGraphEdges.stream()
.filter(otherCallGraphEdges::contains)
.collect(Collectors.toList());
}

/*
In the intersectedMethods() function, we iterate over each node in both call graphs and
return the intersection of the nodes.
*/
public List<MethodSignature> intersectedMethods() {
return baseCallGraph.getMethodSignatures().stream()
.filter(otherCallGraph.getMethodSignatures()::contains)
.collect(Collectors.toList());
}

/*
In the uniqueBaseGraphCalls() function, we iterate over each edges in base call graph and
return the unique edges present in the base call graph.
*/
public List<Pair<MethodSignature, MethodSignature>> uniqueBaseGraphCalls() {
return baseCallGraphEdges.stream()
.filter(edge -> !otherCallGraphEdges.contains(edge))
.collect(Collectors.toList());
}

/*
In the uniqueBaseGraphMethods() function, we iterate over each node in base call graph and
return the unique nodes present in the base call graph.
*/
public List<MethodSignature> uniqueBaseGraphMethods() {
return baseCallGraph.getMethodSignatures().stream()
.filter(node -> !otherCallGraph.getMethodSignatures().contains(node))
.collect(Collectors.toList());
}

/*
In the uniqueOtherGraphCalls() function, we iterate over each edges in other call graph and
return the unique edges present in the other call graph.
*/
public List<Pair<MethodSignature, MethodSignature>> uniqueOtherGraphCalls() {
return otherCallGraphEdges.stream()
.filter(edge -> !baseCallGraphEdges.contains(edge))
.collect(Collectors.toList());
}

/*
In the uniqueOtherGraphMethods() function, we iterate over each node in other call graph and
return the unique nodes present in the other call graph.
*/
public List<MethodSignature> uniqueOtherGraphMethods() {
return otherCallGraph.getMethodSignatures().stream()
.filter(node -> !baseCallGraph.getMethodSignatures().contains(node))
.collect(Collectors.toList());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,12 @@ public MutableCallGraph copy() {
(DefaultDirectedGraph<Vertex, Edge>) graph.clone(), new HashMap<>(signatureToVertex));
}

@Nonnull
@Override
public CallGraphDifference diff(@Nonnull CallGraph callGraph) {
return new CallGraphDifference(this, callGraph);
}

/**
* it returns the vertex of the graph that describes the given method signature in the call graph.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package sootup.callgraph;

import java.util.Collections;
import java.util.List;
import org.apache.commons.lang3.tuple.Pair;
import org.junit.jupiter.api.Test;
import sootup.core.inputlocation.AnalysisInputLocation;
import sootup.core.signatures.MethodSignature;
import sootup.java.bytecode.inputlocation.JavaClassPathAnalysisInputLocation;
import sootup.java.core.types.JavaClassType;
import sootup.java.core.views.JavaView;

public class CallGraphDifferenceTest {

@Test
public void testCGDiff() {
String baseDir = "../shared-test-resources/CallGraphDifference/binary/";
AnalysisInputLocation inputLocation =
new JavaClassPathAnalysisInputLocation(baseDir, null, Collections.emptyList());
swissiety marked this conversation as resolved.
Show resolved Hide resolved
JavaView view = new JavaView(inputLocation);

ClassHierarchyAnalysisAlgorithm chaAlgorithm = new ClassHierarchyAnalysisAlgorithm(view);
JavaClassType chaClassType = view.getIdentifierFactory().getClassType("Example");
MethodSignature chaMethodSignature =
view.getIdentifierFactory()
.getMethodSignature(
chaClassType, "main", "void", Collections.singletonList("java.lang.String[]"));
CallGraph cg1 = chaAlgorithm.initialize(Collections.singletonList(chaMethodSignature));

RapidTypeAnalysisAlgorithm rtaAlgorithm = new RapidTypeAnalysisAlgorithm(view);
CallGraph cg2 = rtaAlgorithm.initialize(Collections.singletonList(chaMethodSignature));

CallGraphDifference callGraphDifference = cg1.diff(cg2);
System.out.println("Unique Base Graph Calls/Edges");
List<Pair<MethodSignature, MethodSignature>> uniqueBaseGraphCalls =
callGraphDifference.uniqueBaseGraphCalls();
uniqueBaseGraphCalls.forEach(System.out::println);
System.out.println("Unique Base Graph Methods/Nodes");
List<MethodSignature> uniqueBaseGraphMethods = callGraphDifference.uniqueBaseGraphMethods();
uniqueBaseGraphMethods.forEach(System.out::println);

System.out.println("Unique Other Graph Calls/Edges");
List<Pair<MethodSignature, MethodSignature>> uniqueOtherGraphCalls =
callGraphDifference.uniqueOtherGraphCalls();
uniqueOtherGraphCalls.forEach(System.out::println);
System.out.println("Unique Other Graph Methods/Nodes");
List<MethodSignature> uniqueOtherGraphMethods = callGraphDifference.uniqueOtherGraphMethods();
uniqueOtherGraphMethods.forEach(System.out::println);

System.out.println("Intersected Calls/Edges");
List<Pair<MethodSignature, MethodSignature>> intersectedCalls =
callGraphDifference.intersectedCalls();
intersectedCalls.forEach(System.out::println);
System.out.println("Intersected Methods/Nodes");
List<MethodSignature> intersectedMethods = callGraphDifference.intersectedMethods();
intersectedMethods.forEach(System.out::println);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@
import qilin.util.DataFactory;
import qilin.util.queue.ChunkedQueue;
import qilin.util.queue.QueueReader;
import sootup.callgraph.CallGraph;
import sootup.callgraph.CallGraphDifference;
import sootup.callgraph.MutableCallGraph;
import sootup.core.jimple.common.stmt.Stmt;
import sootup.core.model.SootMethod;
Expand Down Expand Up @@ -450,6 +452,12 @@ public MutableCallGraph copy() {
throw new UnsupportedOperationException();
}

@Nonnull
@Override
public CallGraphDifference diff(@Nonnull CallGraph callGraph) {
throw new UnsupportedOperationException();
}

@Override
public boolean containsMethod(@Nonnull MethodSignature method) {
return this.methods.contains(method);
Expand Down
Loading