Skip to content

Commit

Permalink
Merge branch 'develop' into th_lca
Browse files Browse the repository at this point in the history
  • Loading branch information
swissiety authored Oct 1, 2024
2 parents ffee3f1 + 9369a01 commit e5a70a3
Show file tree
Hide file tree
Showing 22 changed files with 393 additions and 73 deletions.
2 changes: 1 addition & 1 deletion docs/callgraphs.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Below, we show how to create a type hierarchy:
String cpString = "src/test/resources/Callgraph/binary";
List<AnalysisInputLocation> inputLocations = new ArrayList();
inputLocations.add(new JavaClassPathAnalysisInputLocation(cpStr));
inputLocations.add(new DefaultRTJarAnalysisInputLocation());
inputLocations.add(new DefaultRuntimeAnalysisInputLocation());

JavaView view = new JavaView(inputLocations);
```
Expand Down
2 changes: 1 addition & 1 deletion docs/typehierarchy.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ The TypeHierarchy models the relationship of Classes or Interfaces of a OOP prog
String cpString = "src/test/resources/Callgraph/binary";
List<AnalysisInputLocation> inputLocations = new ArrayList();
inputLocations.add(new JavaClassPathAnalysisInputLocation(cpStr));
inputLocations.add(new DefaultRTJarAnalysisInputLocation());
inputLocations.add(new DefaultRuntimeAnalysisInputLocation());

JavaView view = new JavaView(inputLocations);
TypeHierarchy typehierarchy = view.getTypeHierarchy();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
package sootup.analysis.interprocedural.icfg;

import java.util.Collection;
import java.util.List;
import java.util.Set;
import sootup.core.graph.StmtGraph;
import sootup.core.jimple.basic.Value;
import sootup.core.jimple.common.stmt.Stmt;
import sootup.core.model.SootMethod;

/**
* Same as {@link JimpleBasedInterproceduralCFG} but based on inverted Stmt graphs. This should be
* used for backward analyses.
*/
public class BackwardsInterproceduralCFG implements BiDiInterproceduralCFG<Stmt, SootMethod> {

protected final BiDiInterproceduralCFG<Stmt, SootMethod> delegate;

public BackwardsInterproceduralCFG(BiDiInterproceduralCFG<Stmt, SootMethod> fwICFG) {
delegate = fwICFG;
}

// swapped
@Override
public List<Stmt> getSuccsOf(Stmt n) {
return delegate.getPredsOf(n);
}

// swapped
@Override
public Collection<Stmt> getStartPointsOf(SootMethod m) {
return delegate.getEndPointsOf(m);
}

// swapped
@Override
public List<Stmt> getReturnSitesOfCallAt(Stmt n) {
return delegate.getPredsOfCallAt(n);
}

// swapped
@Override
public boolean isExitStmt(Stmt stmt) {
return delegate.isStartPoint(stmt);
}

// swapped
@Override
public boolean isStartPoint(Stmt stmt) {
return delegate.isExitStmt(stmt);
}

// swapped
@Override
public Set<Stmt> allNonCallStartNodes() {
return delegate.allNonCallEndNodes();
}

// swapped
@Override
public List<Stmt> getPredsOf(Stmt u) {
return delegate.getSuccsOf(u);
}

// swapped
@Override
public Collection<Stmt> getEndPointsOf(SootMethod m) {
return delegate.getStartPointsOf(m);
}

// swapped
@Override
public List<Stmt> getPredsOfCallAt(Stmt u) {
return delegate.getSuccsOf(u);
}

// swapped
@Override
public Set<Stmt> allNonCallEndNodes() {
return delegate.allNonCallStartNodes();
}

// same
@Override
public SootMethod getMethodOf(Stmt n) {
return delegate.getMethodOf(n);
}

// same
@Override
public Collection<SootMethod> getCalleesOfCallAt(Stmt n) {
return delegate.getCalleesOfCallAt(n);
}

// same
@Override
public Collection<Stmt> getCallersOf(SootMethod m) {
return delegate.getCallersOf(m);
}

// same
@Override
public Set<Stmt> getCallsFromWithin(SootMethod m) {
return delegate.getCallsFromWithin(m);
}

// same
@Override
public boolean isCallStmt(Stmt stmt) {
return delegate.isCallStmt(stmt);
}

// same
@Override
public StmtGraph<?> getOrCreateStmtGraph(SootMethod m) {
return delegate.getOrCreateStmtGraph(m);
}

// same
@Override
public List<Value> getParameterRefs(SootMethod m) {
return delegate.getParameterRefs(m);
}

@Override
public boolean isFallThroughSuccessor(Stmt stmt, Stmt succ) {
throw new UnsupportedOperationException("not implemented because semantics unclear");
}

@Override
public boolean isBranchTarget(Stmt stmt, Stmt succ) {
throw new UnsupportedOperationException("not implemented because semantics unclear");
}

// swapped
@Override
public boolean isReturnSite(Stmt n) {
for (Stmt pred : getSuccsOf(n)) {
if (isCallStmt(pred)) {
return true;
}
}
return false;
}

// same
@Override
public boolean isReachable(Stmt u) {
return delegate.isReachable(u);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sootup.callgraph.CallGraph;
import sootup.callgraph.CallGraphAlgorithm;
import sootup.callgraph.ClassHierarchyAnalysisAlgorithm;
import sootup.core.graph.StmtGraph;
import sootup.core.jimple.common.stmt.Stmt;
Expand All @@ -56,7 +55,6 @@ public class JimpleBasedInterproceduralCFG extends AbstractJimpleBasedICFG {

protected static final Logger logger =
LoggerFactory.getLogger(JimpleBasedInterproceduralCFG.class);
private final MethodSignature mainMethodSignature;

protected boolean includeReflectiveCalls;

Expand Down Expand Up @@ -109,60 +107,72 @@ public Collection<Stmt> load(SootMethod method) {

public JimpleBasedInterproceduralCFG(
View view,
MethodSignature mainMethodSignature,
List<MethodSignature> cgEntryPoints,
boolean enableExceptions,
boolean includeReflectiveCalls) {
this(
new ClassHierarchyAnalysisAlgorithm(view).initialize(cgEntryPoints),
view,
enableExceptions,
includeReflectiveCalls);
}

public JimpleBasedInterproceduralCFG(
CallGraph cg, View view, boolean enableExceptions, boolean includeReflectiveCalls) {
super(enableExceptions);
this.includeReflectiveCalls = includeReflectiveCalls;
this.view = view;
this.mainMethodSignature = mainMethodSignature;
cg = initCallGraph();
this.cg = cg;
initializeStmtToOwner();
}

public CallGraph getCg() {
return cg;
}

public String buildICFGGraph(CallGraph callGraph) {
Map<MethodSignature, StmtGraph<?>> signatureToStmtGraph = new LinkedHashMap<>();
computeAllCalls(mainMethodSignature, signatureToStmtGraph, callGraph);
computeAllCalls(callGraph.getEntryMethods(), signatureToStmtGraph, callGraph);
return ICFGDotExporter.buildICFGGraph(signatureToStmtGraph, view, callGraph);
}

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

private void computeAllCalls(
MethodSignature methodSignature,
List<MethodSignature> entryPoints,
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;
}
if (methodOpt.isPresent()) {
SootMethod sootMethod = methodOpt.get();
if (sootMethod.hasBody()) {
StmtGraph<?> stmtGraph = sootMethod.getBody().getStmtGraph();
signatureToStmtGraph.put(methodSignature, stmtGraph);
visitedMethods.addAll(entryPoints);
for (MethodSignature methodSignature : entryPoints) {
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;
}
if (methodOpt.isPresent()) {
SootMethod sootMethod = methodOpt.get();
if (sootMethod.hasBody()) {
StmtGraph<?> stmtGraph = sootMethod.getBody().getStmtGraph();
signatureToStmtGraph.put(methodSignature, stmtGraph);
}
}
callGraph.callTargetsFrom(methodSignature).stream()
.filter(methodSignature1 -> !visitedMethods.contains(methodSignature1))
.forEach(
nextMethodSignature ->
computeAllCalls(
Collections.singletonList(nextMethodSignature),
signatureToStmtGraph,
callGraph,
visitedMethods));
}
callGraph.callTargetsFrom(methodSignature).stream()
.filter(methodSignature1 -> !visitedMethods.contains(methodSignature1))
.forEach(
nextMethodSignature ->
computeAllCalls(
nextMethodSignature, signatureToStmtGraph, callGraph, visitedMethods));
}

private CallGraph initCallGraph() {
CallGraphAlgorithm cga = new ClassHierarchyAnalysisAlgorithm(view);
return cga.initialize(Collections.singletonList(mainMethodSignature));
}

protected void initializeStmtToOwner() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package sootup.analysis.interprocedural.icfg;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.util.*;
import java.util.stream.Collectors;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import sootup.core.inputlocation.AnalysisInputLocation;
import sootup.core.jimple.common.stmt.Stmt;
import sootup.core.model.SootClass;
import sootup.core.model.SootMethod;
import sootup.core.signatures.MethodSignature;
import sootup.java.bytecode.frontend.inputlocation.JavaClassPathAnalysisInputLocation;
import sootup.java.core.JavaIdentifierFactory;
import sootup.java.core.JavaSootMethod;
import sootup.java.core.types.JavaClassType;
import sootup.java.core.views.JavaView;

@Tag("Java8")
class BackwardsInterproceduralCFGTest {
protected static JavaView view;
protected static MethodSignature entryMethodSignature;
protected static SootMethod entryMethod;

@BeforeAll
public static void setup() {
List<AnalysisInputLocation> inputLocations = new ArrayList<>();
inputLocations.add(new JavaClassPathAnalysisInputLocation("src/test/resources/icfg/binary"));
view = new JavaView(inputLocations);
JavaIdentifierFactory identifierFactory = JavaIdentifierFactory.getInstance();
JavaClassType mainClassSignature =
identifierFactory.getClassType("ICFGExampleForInvokableStmt");
SootClass sc = view.getClass(mainClassSignature).get();
entryMethod =
sc.getMethods().stream().filter(e -> e.getName().equals("entryPoint")).findFirst().get();
entryMethodSignature = entryMethod.getSignature();
}

@Test
void methodStartAndEndPointTest() {
JimpleBasedInterproceduralCFG forwardICFG =
new JimpleBasedInterproceduralCFG(
view, Collections.singletonList(entryMethodSignature), false, false);

BackwardsInterproceduralCFG backwardsInterproceduralCFG =
new BackwardsInterproceduralCFG(forwardICFG);

assertEquals(
forwardICFG.getStartPointsOf(entryMethod),
backwardsInterproceduralCFG.getEndPointsOf(entryMethod));
assertEquals(
forwardICFG.getEndPointsOf(entryMethod),
backwardsInterproceduralCFG.getStartPointsOf(entryMethod));
}

@Test
void methodToCallerStmtTest() {
JimpleBasedInterproceduralCFG forwardICFG =
new JimpleBasedInterproceduralCFG(
view, Collections.singletonList(entryMethodSignature), false, false);

BackwardsInterproceduralCFG backwardsInterproceduralCFG =
new BackwardsInterproceduralCFG(forwardICFG);

MethodSignature sig =
JavaIdentifierFactory.getInstance()
.getMethodSignature(
"ICFGExampleForInvokableStmt",
"foo",
"void",
Collections.singletonList("java.lang.String"));
Optional<JavaSootMethod> methodOpt = view.getMethod(sig);
assertTrue(methodOpt.isPresent());
Collection<Stmt> callersOf = backwardsInterproceduralCFG.getCallersOf(methodOpt.get());
assertEquals(3, callersOf.size());
Set<MethodSignature> methodSignatures =
callersOf.stream()
.map(c -> c.asInvokableStmt().getInvokeExpr().get().getMethodSignature())
.collect(Collectors.toSet());
assertTrue(methodSignatures.contains(sig));
}
}
Loading

0 comments on commit e5a70a3

Please sign in to comment.