diff --git a/sootup.analysis/src/main/java/sootup/analysis/interprocedural/icfg/AbstractJimpleBasedICFG.java b/sootup.analysis/src/main/java/sootup/analysis/interprocedural/icfg/AbstractJimpleBasedICFG.java
index 6f692f1a6ff..753140915f7 100644
--- a/sootup.analysis/src/main/java/sootup/analysis/interprocedural/icfg/AbstractJimpleBasedICFG.java
+++ b/sootup.analysis/src/main/java/sootup/analysis/interprocedural/icfg/AbstractJimpleBasedICFG.java
@@ -49,6 +49,7 @@ public abstract class AbstractJimpleBasedICFG implements BiDiInterproceduralCFG<
protected LoadingCache
> bodyToStmtGraph =
IDESolver.DEFAULT_CACHE_BUILDER.build(
new CacheLoader>() {
+ @Nonnull
@Override
public StmtGraph> load(@Nonnull Body body) {
return makeGraph(body);
@@ -59,6 +60,7 @@ public StmtGraph> load(@Nonnull Body body) {
protected LoadingCache> methodToParameterRefs =
IDESolver.DEFAULT_CACHE_BUILDER.build(
new CacheLoader>() {
+ @Nonnull
@Override
public List load(@Nonnull SootMethod m) {
return new ArrayList<>(m.getBody().getParameterLocals());
@@ -69,6 +71,7 @@ public List load(@Nonnull SootMethod m) {
protected LoadingCache> methodToCallsFromWithin =
IDESolver.DEFAULT_CACHE_BUILDER.build(
new CacheLoader>() {
+ @Nonnull
@Override
public Set load(@Nonnull SootMethod m) {
return getCallsFromWithinMethod(m);
@@ -185,7 +188,7 @@ public boolean setOwnerStatement(Stmt u, Body b) {
@Override
public boolean isCallStmt(Stmt stmt) {
- return stmt.containsInvokeExpr();
+ return stmt.isInvokableStmt() && stmt.asInvokableStmt().containsInvokeExpr();
}
@Override
diff --git a/sootup.analysis/src/main/java/sootup/analysis/interprocedural/icfg/CGEdgeUtil.java b/sootup.analysis/src/main/java/sootup/analysis/interprocedural/icfg/CGEdgeUtil.java
index 9c938671ebf..50194965d8e 100644
--- a/sootup.analysis/src/main/java/sootup/analysis/interprocedural/icfg/CGEdgeUtil.java
+++ b/sootup.analysis/src/main/java/sootup/analysis/interprocedural/icfg/CGEdgeUtil.java
@@ -59,12 +59,11 @@ public static Set> getCallEdges(
SootMethod method = view.getMethod(caller).orElse(null);
if (method != null && method.hasBody()) {
for (Stmt s : method.getBody().getStmtGraph().getNodes()) {
- if (s.containsInvokeExpr()) {
+ if (s.isInvokableStmt() && s.asInvokableStmt().containsInvokeExpr()) {
+ AbstractInvokeExpr invokeExpr = s.asInvokableStmt().getInvokeExpr().get();
CalleeMethodSignature callee =
new CalleeMethodSignature(
- s.getInvokeExpr().getMethodSignature(),
- findCallGraphEdgeType(s.getInvokeExpr()),
- s);
+ invokeExpr.getMethodSignature(), findCallGraphEdgeType(invokeExpr), s);
callEdges.add(new ImmutablePair<>(caller, callee));
}
}
@@ -116,7 +115,7 @@ public enum CallGraphEdgeType {
/** Due to call to Class.newInstance(..) when reflection log is enabled. */
REFL_CLASS_NEWINSTANCE("REFL_CLASS_NEWINSTANCE");
- private String name;
+ private final String name;
CallGraphEdgeType(String name) {
this.name = name;
diff --git a/sootup.analysis/src/main/java/sootup/analysis/interprocedural/icfg/ICFGDotExporter.java b/sootup.analysis/src/main/java/sootup/analysis/interprocedural/icfg/ICFGDotExporter.java
index d7a1f736cb0..b7d6a285a2b 100644
--- a/sootup.analysis/src/main/java/sootup/analysis/interprocedural/icfg/ICFGDotExporter.java
+++ b/sootup.analysis/src/main/java/sootup/analysis/interprocedural/icfg/ICFGDotExporter.java
@@ -72,8 +72,9 @@ public static Map computeCalls(
for (BasicBlock> block : blocks) {
List stmts = block.getStmts();
for (Stmt stmt : stmts) {
- if (stmt.containsInvokeExpr()) {
- MethodSignature target = stmt.getInvokeExpr().getMethodSignature();
+ if (stmt.isInvokableStmt() && stmt.asInvokableStmt().containsInvokeExpr()) {
+ MethodSignature target =
+ stmt.asInvokableStmt().getInvokeExpr().get().getMethodSignature();
int hashCode = stmt.hashCode();
calls.put(hashCode, target);
// compute all the classes that are made to the subclasses as well
@@ -112,7 +113,7 @@ public static Set getMethodSignatureInSubClass(
if (!callGraph.containsMethod(source) || !callGraph.containsMethod(target)) {
return Collections.emptySet();
}
- return callGraph.callsFrom(source).stream()
+ return callGraph.callTargetsFrom(source).stream()
.filter(
methodSignature ->
!methodSignature.equals(target)
diff --git a/sootup.analysis/src/main/java/sootup/analysis/interprocedural/icfg/JimpleBasedInterproceduralCFG.java b/sootup.analysis/src/main/java/sootup/analysis/interprocedural/icfg/JimpleBasedInterproceduralCFG.java
index 3606614af65..141a4101e4a 100644
--- a/sootup.analysis/src/main/java/sootup/analysis/interprocedural/icfg/JimpleBasedInterproceduralCFG.java
+++ b/sootup.analysis/src/main/java/sootup/analysis/interprocedural/icfg/JimpleBasedInterproceduralCFG.java
@@ -40,6 +40,8 @@
import sootup.callgraph.CallGraphAlgorithm;
import sootup.callgraph.ClassHierarchyAnalysisAlgorithm;
import sootup.core.graph.StmtGraph;
+import sootup.core.jimple.common.expr.AbstractInvokeExpr;
+import sootup.core.jimple.common.stmt.InvokableStmt;
import sootup.core.jimple.common.stmt.Stmt;
import sootup.core.model.SootMethod;
import sootup.core.signatures.MethodSignature;
@@ -71,7 +73,9 @@ public class JimpleBasedInterproceduralCFG extends AbstractJimpleBasedICFG {
@Override
public Collection load(Stmt stmt) {
ArrayList res = new ArrayList<>();
- MethodSignature methodSignature = stmt.getInvokeExpr().getMethodSignature();
+ if (!stmt.isInvokableStmt() && !stmt.asInvokableStmt().containsInvokeExpr()) return res;
+ MethodSignature methodSignature =
+ stmt.asInvokableStmt().getInvokeExpr().get().getMethodSignature();
Optional extends SootMethod> smOpt = view.getMethod(methodSignature);
if (smOpt.isPresent()) {
SootMethod sm = smOpt.get();
@@ -99,7 +103,7 @@ public Collection load(SootMethod method) {
ArrayList res = new ArrayList<>();
// only retain callers that are explicit call sites or
// Thread.start()
- Set callsToMethod = cg.callsTo(method.getSignature());
+ Set callsToMethod = cg.callSourcesTo(method.getSignature());
for (MethodSignature methodSignature : callsToMethod) {
Stmt stmt = filterEdgeAndGetCallerStmt(methodSignature);
if (stmt != null) {
@@ -179,7 +183,7 @@ private void computeAllCalls(
signatureToStmtGraph.put(methodSignature, stmtGraph);
}
}
- callGraph.callsFrom(methodSignature).stream()
+ callGraph.callTargetsFrom(methodSignature).stream()
.filter(methodSignature1 -> !visitedMethods.contains(methodSignature1))
.forEach(
nextMethodSignature ->
@@ -219,12 +223,13 @@ public static Set> getCallEdges(
final SootMethod method = methodOpt.get();
if (method.hasBody()) {
for (Stmt s : method.getBody().getStmtGraph().getNodes()) {
- if (s.containsInvokeExpr()) {
+ // TODO: Consider calls to clinit methods caused by static fields
+ // Assignment statements without invokeExpressions
+ if (s instanceof InvokableStmt && ((InvokableStmt) s).containsInvokeExpr()) {
+ AbstractInvokeExpr expr = ((InvokableStmt) s).getInvokeExpr().get();
CalleeMethodSignature callee =
new CalleeMethodSignature(
- s.getInvokeExpr().getMethodSignature(),
- CGEdgeUtil.findCallGraphEdgeType(s.getInvokeExpr()),
- s);
+ expr.getMethodSignature(), CGEdgeUtil.findCallGraphEdgeType(expr), s);
callEdges.add(new ImmutablePair<>(caller, callee));
}
}
diff --git a/sootup.analysis/src/test/java/sootup/analysis/interprocedural/ifds/CGEdgeUtilTest.java b/sootup.analysis/src/test/java/sootup/analysis/interprocedural/ifds/CGEdgeUtilTest.java
index f2a1a5ff972..df115508df5 100644
--- a/sootup.analysis/src/test/java/sootup/analysis/interprocedural/ifds/CGEdgeUtilTest.java
+++ b/sootup.analysis/src/test/java/sootup/analysis/interprocedural/ifds/CGEdgeUtilTest.java
@@ -29,6 +29,7 @@
import sootup.core.jimple.common.expr.JSpecialInvokeExpr;
import sootup.core.jimple.common.expr.JStaticInvokeExpr;
import sootup.core.jimple.common.expr.JVirtualInvokeExpr;
+import sootup.core.jimple.common.stmt.InvokableStmt;
import sootup.core.jimple.common.stmt.Stmt;
import sootup.core.jimple.visitor.ExprVisitor;
import sootup.core.model.SootClass;
@@ -163,7 +164,9 @@ public void testGetCallEdges() {
List invokesStmts =
m.getBody().getStmts().stream()
- .filter(Stmt::containsInvokeExpr)
+ .filter(Stmt::isInvokableStmt)
+ .map(Stmt::asInvokableStmt)
+ .filter(InvokableStmt::containsInvokeExpr)
.collect(Collectors.toList());
assertEquals(invokesStmts.size(), 3);
MethodSignature constructorMethodSignature =
@@ -222,7 +225,10 @@ private void checkPair(
assertNotNull(virtualCall);
Stmt virtualStmt =
invokesStmts.stream()
- .filter(stmt -> stmt.getInvokeExpr().getClass() == invokeClass)
+ .filter(Stmt::isInvokableStmt)
+ .map(Stmt::asInvokableStmt)
+ .filter(InvokableStmt::containsInvokeExpr)
+ .filter(stmt -> stmt.getInvokeExpr().get().getClass() == invokeClass)
.findAny()
.orElse(null);
assertNotNull(virtualStmt);
diff --git a/sootup.analysis/src/test/java/sootup/analysis/interprocedural/ifds/IFDSTaintAnalysisProblem.java b/sootup.analysis/src/test/java/sootup/analysis/interprocedural/ifds/IFDSTaintAnalysisProblem.java
index 4e6ba910147..f3392c84fc6 100644
--- a/sootup.analysis/src/test/java/sootup/analysis/interprocedural/ifds/IFDSTaintAnalysisProblem.java
+++ b/sootup.analysis/src/test/java/sootup/analysis/interprocedural/ifds/IFDSTaintAnalysisProblem.java
@@ -46,7 +46,7 @@
public class IFDSTaintAnalysisProblem
extends DefaultJimpleIFDSTabulationProblem> {
- private SootMethod entryMethod;
+ private final SootMethod entryMethod;
protected InterproceduralCFG icfg;
@@ -74,18 +74,18 @@ public FlowFunction getNormalFlowFunction(Stmt curr, Stmt succ) {
@Override
public FlowFunction getCallFlowFunction(Stmt callStmt, SootMethod destinationMethod) {
- return getCallFlow(callStmt, destinationMethod);
+ return getCallFlow(callStmt.asInvokableStmt(), destinationMethod);
}
@Override
public FlowFunction getReturnFlowFunction(
Stmt callSite, SootMethod calleeMethod, Stmt exitStmt, Stmt returnSite) {
- return getReturnFlow(callSite, calleeMethod, exitStmt, returnSite);
+ return getReturnFlow(callSite.asInvokableStmt(), calleeMethod, exitStmt, returnSite);
}
@Override
public FlowFunction getCallToReturnFlowFunction(Stmt callSite, Stmt returnSite) {
- return getCallToReturnFlow(callSite, returnSite);
+ return getCallToReturnFlow(callSite.asInvokableStmt(), returnSite);
}
};
}
@@ -107,36 +107,33 @@ FlowFunction getNormalFlow(Stmt curr, Stmt succ) {
return new Gen<>(leftOp, zeroValue());
}
}
- return new FlowFunction() {
- @Override
- public Set computeTargets(Value source) {
- // source = {v.f*} some local and all its fields
- // Kill T = ...
- if (source == leftOp) {
- return Collections.emptySet();
- }
- Set res = new HashSet();
- res.add(source);
- // x = T
- if (source == rightOp) {
- res.add(leftOp);
- }
- return res;
+ return source -> {
+ // source = {v.f*} some local and all its fields
+ // Kill T = ...
+ if (source == leftOp) {
+ return Collections.emptySet();
+ }
+ Set res = new HashSet<>();
+ res.add(source);
+ // x = T
+ if (source == rightOp) {
+ res.add(leftOp);
}
+ return res;
};
}
return Identity.v();
}
- FlowFunction getCallFlow(Stmt callStmt, final SootMethod destinationMethod) {
+ FlowFunction getCallFlow(InvokableStmt callStmt, final SootMethod destinationMethod) {
if ("".equals(destinationMethod.getName())) {
return KillAll.v();
}
- AbstractInvokeExpr ie = callStmt.getInvokeExpr();
+ AbstractInvokeExpr ie = callStmt.getInvokeExpr().get();
final List callArgs = ie.getArgs();
- final List paramLocals = new ArrayList();
+ final List paramLocals = new ArrayList<>();
for (int i = 0; i < destinationMethod.getParameterCount(); i++) {
paramLocals.add(destinationMethod.getBody().getParameterLocal(i));
}
@@ -154,28 +151,25 @@ FlowFunction getCallFlow(Stmt callStmt, final SootMethod destinationMetho
}
final Value baseF = base;
- return new FlowFunction() {
- @Override
- public Set computeTargets(Value source) {
- Set ret = new HashSet<>();
- if (source instanceof JStaticFieldRef) {
- ret.add(source);
- }
- // Tainted func parameters
- for (int i = 0; i < callArgs.size(); i++) {
- if (callArgs.get(i).equivTo(source) && i < paramLocals.size()) {
- ret.add(paramLocals.get(i));
- }
+ return source -> {
+ Set ret = new HashSet<>();
+ if (source instanceof JStaticFieldRef) {
+ ret.add(source);
+ }
+ // Tainted func parameters
+ for (int i = 0; i < callArgs.size(); i++) {
+ if (callArgs.get(i).equivTo(source) && i < paramLocals.size()) {
+ ret.add(paramLocals.get(i));
}
- return ret;
}
+ return ret;
};
}
FlowFunction getReturnFlow(
- final Stmt callSite, final SootMethod calleeMethod, Stmt exitStmt, Stmt returnSite) {
+ final InvokableStmt callSite, final SootMethod calleeMethod, Stmt exitStmt, Stmt returnSite) {
- AbstractInvokeExpr ie = callSite.getInvokeExpr();
+ AbstractInvokeExpr ie = callSite.getInvokeExpr().get();
Value base = null;
if (ie instanceof JVirtualInvokeExpr) {
@@ -203,44 +197,38 @@ FlowFunction getReturnFlow(
}
}
}
- return new FlowFunction() {
- @Override
- public Set computeTargets(Value source) {
- Set ret = new HashSet<>();
- if (source instanceof JStaticFieldRef) {
- ret.add(source);
- }
- if (callSite instanceof AbstractDefinitionStmt && source == retOp) {
- AbstractDefinitionStmt defnStmt = (AbstractDefinitionStmt) callSite;
- ret.add(defnStmt.getLeftOp());
- }
- if (baseF != null && source.equals(calleeMethod.getBody().getThisLocal())) {
- ret.add(baseF);
- }
- return ret;
+ return source -> {
+ Set ret = new HashSet<>();
+ if (source instanceof JStaticFieldRef) {
+ ret.add(source);
+ }
+ if (callSite instanceof AbstractDefinitionStmt && source == retOp) {
+ AbstractDefinitionStmt defnStmt = (AbstractDefinitionStmt) callSite;
+ ret.add(defnStmt.getLeftOp());
}
+ if (baseF != null && source.equals(calleeMethod.getBody().getThisLocal())) {
+ ret.add(baseF);
+ }
+ return ret;
};
}
if (exitStmt instanceof JReturnVoidStmt) {
- return new FlowFunction() {
- @Override
- public Set computeTargets(Value source) {
- Set ret = new HashSet();
- if (source instanceof JStaticFieldRef) {
- ret.add(source);
- }
- if (baseF != null && source.equals(calleeMethod.getBody().getThisLocal())) {
- ret.add(baseF);
- }
- return ret;
+ return source -> {
+ Set ret = new HashSet<>();
+ if (source instanceof JStaticFieldRef) {
+ ret.add(source);
}
+ if (baseF != null && source.equals(calleeMethod.getBody().getThisLocal())) {
+ ret.add(baseF);
+ }
+ return ret;
};
}
return KillAll.v();
}
- FlowFunction getCallToReturnFlow(final Stmt callSite, Stmt returnSite) {
- AbstractInvokeExpr ie = callSite.getInvokeExpr();
+ FlowFunction getCallToReturnFlow(final InvokableStmt callSite, Stmt returnSite) {
+ AbstractInvokeExpr ie = callSite.getInvokeExpr().get();
final List callArgs = ie.getArgs();
Value base = null;
@@ -267,25 +255,22 @@ FlowFunction getCallToReturnFlow(final Stmt callSite, Stmt returnSite) {
// use assumption if no callees to analyze
if (icfg.getCalleesOfCallAt(callSite).isEmpty()) {
- return new FlowFunction() {
- @Override
- public Set computeTargets(Value source) {
- Set ret = new HashSet();
- ret.add(source);
- // taint leftOp if base is tainted
- if (baseF != null && leftOpF != null && source == baseF) {
- ret.add(leftOpF);
- }
- // taint leftOp if one of the args is tainted
- if (leftOpF != null && callArgs.contains(source)) {
- ret.add(leftOpF);
- }
- // taint base if one of the args is tainted and has no callee in known methods
- if (baseF != null && callArgs.contains(source)) {
- ret.add(baseF);
- }
- return ret;
+ return source -> {
+ Set ret = new HashSet<>();
+ ret.add(source);
+ // taint leftOp if base is tainted
+ if (baseF != null && leftOpF != null && source == baseF) {
+ ret.add(leftOpF);
+ }
+ // taint leftOp if one of the args is tainted
+ if (leftOpF != null && callArgs.contains(source)) {
+ ret.add(leftOpF);
}
+ // taint base if one of the args is tainted and has no callee in known methods
+ if (baseF != null && callArgs.contains(source)) {
+ ret.add(baseF);
+ }
+ return ret;
};
}
return Identity.v();
diff --git a/sootup.callgraph/src/main/java/sootup/callgraph/AbstractCallGraphAlgorithm.java b/sootup.callgraph/src/main/java/sootup/callgraph/AbstractCallGraphAlgorithm.java
index ad4b1dcaf95..be599ea6adc 100644
--- a/sootup.callgraph/src/main/java/sootup/callgraph/AbstractCallGraphAlgorithm.java
+++ b/sootup.callgraph/src/main/java/sootup/callgraph/AbstractCallGraphAlgorithm.java
@@ -28,11 +28,13 @@
import javax.annotation.Nonnull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import sootup.callgraph.CallGraph.Call;
import sootup.core.IdentifierFactory;
import sootup.core.jimple.basic.Value;
import sootup.core.jimple.common.expr.AbstractInvokeExpr;
import sootup.core.jimple.common.expr.JStaticInvokeExpr;
import sootup.core.jimple.common.ref.JStaticFieldRef;
+import sootup.core.jimple.common.stmt.InvokableStmt;
import sootup.core.jimple.common.stmt.JAssignStmt;
import sootup.core.jimple.common.stmt.Stmt;
import sootup.core.model.Method;
@@ -74,13 +76,14 @@ protected AbstractCallGraphAlgorithm(@Nonnull View view) {
*/
@Nonnull
final CallGraph constructCompleteCallGraph(View view, List entryPoints) {
- MutableCallGraph cg = initializeCallGraph();
-
Deque workList = new ArrayDeque<>(entryPoints);
Set processed = new HashSet<>();
- // implicit edge from entry point to static initializer
- addImplicitEdgesOfEntryPoints(entryPoints, cg, workList);
+ // find additional entry points
+ List clinits = getClinitFromEntryPoints(entryPoints);
+
+ workList.addAll(clinits);
+ MutableCallGraph cg = initializeCallGraph(entryPoints, clinits);
processWorkList(view, workList, processed, cg);
return cg;
@@ -92,41 +95,32 @@ final CallGraph constructCompleteCallGraph(View view, List entr
*
* @return the initialized call graph used in the call graph algorithm
*/
- protected MutableCallGraph initializeCallGraph() {
- return new GraphBasedCallGraph();
+ protected MutableCallGraph initializeCallGraph(
+ List entryPoints, List clinits) {
+ ArrayList rootSignatures = new ArrayList<>(entryPoints);
+ rootSignatures.addAll(clinits);
+ return new GraphBasedCallGraph(rootSignatures);
}
/**
- * This method adds implicit edges of the entry points of the call graph algorithm. It will add an
- * edge to all static initializer of the entry points.
+ * This method returns a list of static initializers that should be considered by the given entry
+ * points
*
* @param entryPoints the entry points of the call graph algorithm
- * @param cg the call graph which will save the added implicit edges.
- * @param workList the implicit targets will be added to the work list to process in the call
- * graph algorithm
*/
- protected void addImplicitEdgesOfEntryPoints(
- List entryPoints, MutableCallGraph cg, Deque workList) {
- entryPoints.forEach(
- methodSignature -> {
- SootMethod clintMethod =
- view.getMethod(methodSignature.getDeclClassType().getStaticInitializer())
- .orElse(null);
- if (clintMethod == null) {
- return;
- }
- MethodSignature staticInitSig = clintMethod.getSignature();
- if (!cg.containsMethod(methodSignature)) {
- cg.addMethod(methodSignature);
- }
- if (!cg.containsMethod(staticInitSig)) {
- cg.addMethod(staticInitSig);
- }
- if (!cg.containsCall(methodSignature, staticInitSig)) {
- cg.addCall(methodSignature, staticInitSig);
- workList.push(staticInitSig);
- }
- });
+ protected List getClinitFromEntryPoints(List entryPoints) {
+ return entryPoints.stream()
+ .map(
+ methodSignature ->
+ getSignatureOfImplementedStaticInitializer(methodSignature.getDeclClassType()))
+ .filter(Optional::isPresent)
+ .map(Optional::get)
+ .collect(Collectors.toList());
+ }
+
+ private Optional getSignatureOfImplementedStaticInitializer(
+ ClassType classType) {
+ return view.getMethod(classType.getStaticInitializer()).map(SootClassMember::getSignature);
}
/**
@@ -173,24 +167,10 @@ final void processWorkList(
currentClass.getMethod(currentMethodSignature.getSubSignature()).orElse(null);
// get all call targets of invocations in the method body
- Stream invocationTargets = resolveAllCallsFromSourceMethod(currentMethod);
+ resolveAllCallsFromSourceMethod(currentMethod, cg, workList);
// get all call targets of implicit edges in the method body
- Stream implicitTargets =
- resolveAllImplicitCallsFromSourceMethod(view, currentMethod);
-
- // save calls in the call graphs
- Stream.concat(invocationTargets, implicitTargets)
- .forEach(
- t -> {
- if (!cg.containsMethod(t)) {
- cg.addMethod(t);
- }
- if (!cg.containsCall(currentMethodSignature, t)) {
- cg.addCall(currentMethodSignature, t);
- workList.push(t);
- }
- });
+ resolveAllImplicitCallsFromSourceMethod(currentMethod, cg, workList);
// set method as processed
processed.add(currentMethodSignature);
@@ -200,102 +180,170 @@ final void processWorkList(
}
}
+ /**
+ * 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
+ *
+ * @param source the method signature of the caller
+ * @param target the method signature of the callee
+ * @param invokeStmt the stmt causing the call
+ * @param cg the call graph that will be updated
+ * @param workList the worklist in which the method signature of newly added vertexes will be
+ * added
+ */
+ protected void addCallToCG(
+ @Nonnull MethodSignature source,
+ @Nonnull MethodSignature target,
+ @Nonnull InvokableStmt invokeStmt,
+ @Nonnull MutableCallGraph cg,
+ @Nonnull Deque workList) {
+ if (!cg.containsMethod(source)) {
+ cg.addMethod(source);
+ workList.push(source);
+ }
+ if (!cg.containsMethod(target)) {
+ cg.addMethod(target);
+ workList.push(target);
+ }
+ if (!cg.containsCall(source, target, invokeStmt)) {
+ cg.addCall(source, target, invokeStmt);
+ }
+ }
+
/**
* This method resolves all calls from a given source method. resolveCall is called for each
- * invoke statement in the body of the source method that is implemented in the corresponding call
- * graph algorithm.
+ * invokable statements in the body of the source method that is implemented in the corresponding
+ * call graph algorithm. If new methods will be added as vertexes in the call graph, the work list
+ * will be updated
*
* @param sourceMethod this signature is used to access the statements contained method body of
* the specified method
- * @return a stream containing all resolved callable method signatures by the given source method
+ * @param cg the call graph that will receive the found calls
+ * @param workList the work list that will be updated of found target methods
*/
- @Nonnull
- Stream resolveAllCallsFromSourceMethod(SootMethod sourceMethod) {
+ protected void resolveAllCallsFromSourceMethod(
+ SootMethod sourceMethod, MutableCallGraph cg, Deque workList) {
if (sourceMethod == null || !sourceMethod.hasBody()) {
- return Stream.empty();
+ return;
}
- return sourceMethod.getBody().getStmts().stream()
- .filter(Stmt::containsInvokeExpr)
- .flatMap(s -> resolveCall(sourceMethod, s.getInvokeExpr()));
+ sourceMethod.getBody().getStmts().stream()
+ .filter(Stmt::isInvokableStmt)
+ .map(Stmt::asInvokableStmt)
+ .forEach(
+ stmt ->
+ resolveCall(sourceMethod, stmt)
+ .forEach(
+ targetMethod ->
+ addCallToCG(
+ sourceMethod.getSignature(), targetMethod, stmt, cg, workList)));
}
/**
* It resolves all implicit calls caused by the given source method
*
- * @param view it contains the class data
* @param sourceMethod the inspected source method
- * @return a stream containing all method signatures of targets of implicit calls.
+ * @param cg new calls will be added to the call graph
+ * @param workList new target methods will be added to the work list
*/
- @Nonnull
- protected Stream resolveAllImplicitCallsFromSourceMethod(
- View view, SootMethod sourceMethod) {
+ protected void resolveAllImplicitCallsFromSourceMethod(
+ SootMethod sourceMethod, MutableCallGraph cg, Deque workList) {
if (sourceMethod == null || !sourceMethod.hasBody()) {
- return Stream.empty();
+ return;
}
// collect all static initializer calls
- return resolveAllStaticInitializerCallsFromSourceMethod(view, sourceMethod);
+ resolveAllStaticInitializerCalls(sourceMethod, cg, workList);
}
/**
* It resolves all static initializer calls caused by the given source method
*
- * @param view it contains the class data
* @param sourceMethod the inspected source method
- * @return a stream containing all method signatures of targets of implicit calls.
+ * @param cg clinit calls will be added to the call graph
+ * @param workList found clinit methods will be added to the work list
*/
- @Nonnull
- protected Stream resolveAllStaticInitializerCallsFromSourceMethod(
- View view, SootMethod sourceMethod) {
+ protected void resolveAllStaticInitializerCalls(
+ SootMethod sourceMethod, MutableCallGraph cg, Deque workList) {
if (sourceMethod == null || !sourceMethod.hasBody()) {
- return Stream.empty();
+ return;
}
-
- Stream.Builder targetsToStaticInitializer = Stream.builder();
-
InstantiateClassValueVisitor instantiateVisitor = new InstantiateClassValueVisitor();
-
- sourceMethod
- .getBody()
- .getStmts()
+ sourceMethod.getBody().getStmts().stream()
+ .filter(Stmt::isInvokableStmt)
+ .map(Stmt::asInvokableStmt)
.forEach(
- stmt -> {
+ invokableStmt -> {
// static field usage
- if (stmt.containsFieldRef() && stmt.getFieldRef() instanceof JStaticFieldRef) {
- targetsToStaticInitializer.add(
- stmt.getFieldRef().getFieldSignature().getDeclClassType());
+ ClassType targetClass = null;
+ if (invokableStmt.containsFieldRef()
+ && invokableStmt.getFieldRef() instanceof JStaticFieldRef) {
+ targetClass = invokableStmt.getFieldRef().getFieldSignature().getDeclClassType();
+ addStaticInitializerCalls(
+ sourceMethod.getSignature(), targetClass, invokableStmt, cg, workList);
}
-
- // constructor calls
- if (stmt instanceof JAssignStmt) {
- Value rightOp = ((JAssignStmt) stmt).getRightOp();
- instantiateVisitor.init();
- rightOp.accept(instantiateVisitor);
- ClassType classType = instantiateVisitor.getResult();
- if (classType != null) {
- targetsToStaticInitializer.add(classType);
+ // static method
+ if (invokableStmt.containsInvokeExpr()) {
+ // static method call
+ Optional exprOptional = invokableStmt.getInvokeExpr();
+ if (!exprOptional.isPresent()) return;
+ AbstractInvokeExpr expr = exprOptional.get();
+ if (expr instanceof JStaticInvokeExpr) {
+ ClassType newTargetClass = expr.getMethodSignature().getDeclClassType();
+ // checks if the field points to the same clinit
+ if (!newTargetClass.equals(targetClass)) {
+ addStaticInitializerCalls(
+ sourceMethod.getSignature(), newTargetClass, invokableStmt, cg, workList);
+ }
+ }
+ } else {
+ if (invokableStmt instanceof JAssignStmt) {
+ Value rightOp = ((JAssignStmt) invokableStmt).getRightOp();
+ // extract class type out of new, new array and new multi array
+ instantiateVisitor.init();
+ rightOp.accept(instantiateVisitor);
+ 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)) {
+ addStaticInitializerCalls(
+ sourceMethod.getSignature(), newTargetClass, invokableStmt, cg, workList);
+ }
}
- }
-
- // static method calls
- if (stmt.containsInvokeExpr() && stmt.getInvokeExpr() instanceof JStaticInvokeExpr) {
- targetsToStaticInitializer.add(
- stmt.getInvokeExpr().getMethodSignature().getDeclClassType());
}
});
+ }
- return targetsToStaticInitializer
- .build()
- .flatMap(
- classType ->
- Stream.concat(
- Stream.of(classType), view.getTypeHierarchy().superClassesOf(classType)))
- .filter(Objects::nonNull)
+ /**
+ * Adds all static initializer calls of the given targetClass. An edge from the sourceSig to all
+ * clinit methods of the targetClass and Superclasses will be added to the call graph. If new
+ * target methods will be found, the worklist will be updated.
+ *
+ * @param sourceSig the source method causing the static initilzer call
+ * @param targetClass the class that is statically initialized
+ * @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
+ */
+ private void addStaticInitializerCalls(
+ MethodSignature sourceSig,
+ ClassType targetClass,
+ InvokableStmt invokableStmt,
+ MutableCallGraph cg,
+ Deque workList) {
+ // static initializer call of class
+ view.getMethod(targetClass.getStaticInitializer())
+ .ifPresent(
+ targetSig ->
+ addCallToCG(sourceSig, targetSig.getSignature(), invokableStmt, cg, workList));
+ // static initializer calls of all superclasses
+ view.getTypeHierarchy()
+ .superClassesOf(targetClass)
.map(classType -> view.getMethod(classType.getStaticInitializer()))
.filter(Optional::isPresent)
.map(Optional::get)
- .map(SootClassMember::getSignature);
+ .forEach(
+ targetSig ->
+ addCallToCG(sourceSig, targetSig.getSignature(), invokableStmt, cg, workList));
}
/**
@@ -375,8 +423,11 @@ public CallGraph addClass(@Nonnull CallGraph oldCallGraph, @Nonnull JavaClassTyp
clazz.getMethod(overriddenMethodSig.getSubSignature()).get().getSignature();
if (updated.containsMethod(overriddenMethodSig)) {
- for (MethodSignature callingMethodSig : updated.callsTo(overriddenMethodSig)) {
- updated.addCall(callingMethodSig, overridingMethodSig);
+ for (Call calls : updated.callsTo(overriddenMethodSig)) {
+ updated.addCall(
+ calls.getSourceMethodSignature(),
+ overridingMethodSig,
+ calls.getInvokableStmt());
}
}
});
@@ -438,13 +489,13 @@ public MethodSignature findMainMethod() {
* dependable of the applied call graph algorithm. therefore, it is abstract.
*
* @param method the method object that contains the given invoke expression in the body.
- * @param invokeExpr it contains the call which is resolved.
+ * @param invokableStmt it contains the call which is resolved.
* @return a stream of all reachable method signatures defined by the applied call graph
* algorithm.
*/
@Nonnull
protected abstract Stream resolveCall(
- SootMethod method, AbstractInvokeExpr invokeExpr);
+ SootMethod method, InvokableStmt invokableStmt);
/**
* Searches for the signature of the method that is the concrete implementation of m
.
diff --git a/sootup.callgraph/src/main/java/sootup/callgraph/CallGraph.java b/sootup.callgraph/src/main/java/sootup/callgraph/CallGraph.java
index 470a1d92219..0e222948acc 100644
--- a/sootup.callgraph/src/main/java/sootup/callgraph/CallGraph.java
+++ b/sootup.callgraph/src/main/java/sootup/callgraph/CallGraph.java
@@ -22,13 +22,79 @@
* #L%
*/
+import java.util.List;
import java.util.Set;
import javax.annotation.Nonnull;
+import sootup.core.jimple.common.stmt.InvokableStmt;
import sootup.core.signatures.MethodSignature;
/** The interface of all implemented call graph data structures */
public interface CallGraph {
+ class Call {
+ @Nonnull private final MethodSignature sourceMethodSignature;
+ @Nonnull private final MethodSignature targetMethodSignature;
+ @Nonnull private final InvokableStmt invokableStmt;
+
+ public Call(
+ @Nonnull MethodSignature sourceMethodSignature,
+ @Nonnull MethodSignature targetMethodSignature,
+ @Nonnull InvokableStmt invokableStmt) {
+ this.sourceMethodSignature = sourceMethodSignature;
+ this.invokableStmt = invokableStmt;
+ this.targetMethodSignature = targetMethodSignature;
+ }
+
+ @Nonnull
+ public MethodSignature getSourceMethodSignature() {
+ return sourceMethodSignature;
+ }
+
+ @Nonnull
+ public MethodSignature getTargetMethodSignature() {
+ return targetMethodSignature;
+ }
+
+ @Nonnull
+ public InvokableStmt getInvokableStmt() {
+ return invokableStmt;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ Call call = (Call) o;
+ return sourceMethodSignature.equals(call.sourceMethodSignature)
+ && targetMethodSignature.equals(call.targetMethodSignature)
+ && invokableStmt.equals(call.invokableStmt);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = sourceMethodSignature.hashCode();
+ result = 31 * result + targetMethodSignature.hashCode();
+ result = 31 * result + invokableStmt.hashCode();
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "Call:"
+ + sourceMethodSignature
+ + " -> "
+ + targetMethodSignature
+ + " via "
+ + invokableStmt
+ + ";";
+ }
+ }
+
/**
* This method returns method signatures in the call graph. A method signature is a node in the
* call graph.
@@ -46,7 +112,7 @@ public interface CallGraph {
* @return a set of method signatures that are reached by a direct outgoing edge in the call graph
*/
@Nonnull
- Set callsFrom(@Nonnull MethodSignature sourceMethod);
+ Set callTargetsFrom(@Nonnull MethodSignature sourceMethod);
/**
* This method returns all method signatures that call a given method signature. It returns the
@@ -57,7 +123,28 @@ public interface CallGraph {
* graph
*/
@Nonnull
- Set callsTo(@Nonnull MethodSignature targetMethod);
+ Set callSourcesTo(@Nonnull MethodSignature targetMethod);
+
+ /**
+ * This method returns all method signatures that are called by a given method signature. It
+ * returns the targets of outgoing edges of the given node (method signature) in the call graph
+ *
+ * @param sourceMethod the method signature of the requested node in the call graph
+ * @return a set of method signatures that are reached by a direct outgoing edge in the call graph
+ */
+ @Nonnull
+ Set callsFrom(@Nonnull MethodSignature sourceMethod);
+
+ /**
+ * This method returns all method signatures that call a given method signature. It returns the
+ * sources of incoming edges of the given node (method signature) in the call graph
+ *
+ * @param targetMethod the method signature of the requested node in the call graph
+ * @return a set of method signatures that reach the targetMethod by a direct edge in the call
+ * graph
+ */
+ @Nonnull
+ Set callsTo(@Nonnull MethodSignature targetMethod);
/**
* This method checks if a given method signature is a node in the call graph.
@@ -74,10 +161,22 @@ public interface CallGraph {
*
* @param sourceMethod it defines the source node in the call graph
* @param targetMethod it defines the target node in the call graph
+ * @param invokableStmt it defines the invoke stmt of the call
* @return true if the edge is contained in the call graph, otherwise it will be false.
*/
boolean containsCall(
- @Nonnull MethodSignature sourceMethod, @Nonnull MethodSignature targetMethod);
+ @Nonnull MethodSignature sourceMethod,
+ @Nonnull MethodSignature targetMethod,
+ InvokableStmt invokableStmt);
+
+ /**
+ * This method checks if an edge is contained in the call graph. The edge is defined by a source
+ * and target method signature which can be nodes in the call graph
+ *
+ * @param call it defines the requested call in the call graph
+ * @return true if the edge is contained in the call graph, otherwise it will be false.
+ */
+ boolean containsCall(@Nonnull Call call);
/**
* This method counts every edge in the call graph.
@@ -97,6 +196,13 @@ boolean containsCall(
@Nonnull
MutableCallGraph copy();
+ /**
+ * This method returns all entry methods of the call graph
+ *
+ * @return a list of method signatures of entry points of the call graph
+ */
+ List getEntryMethods();
+
/**
* This method compares the difference between the current call graph and call graph passed into
* the argument.
diff --git a/sootup.callgraph/src/main/java/sootup/callgraph/CallGraphDifference.java b/sootup.callgraph/src/main/java/sootup/callgraph/CallGraphDifference.java
index 3aaf15a5fc9..01a605ae633 100644
--- a/sootup.callgraph/src/main/java/sootup/callgraph/CallGraphDifference.java
+++ b/sootup.callgraph/src/main/java/sootup/callgraph/CallGraphDifference.java
@@ -46,7 +46,7 @@ public CallGraphDifference(CallGraph baseCallGraph, CallGraph otherCallGraph) {
private List> constructEdges(CallGraph cg) {
List> cgEdges = new ArrayList<>();
for (MethodSignature srcNode : cg.getMethodSignatures()) {
- Set outNodes = cg.callsFrom(srcNode);
+ Set outNodes = cg.callTargetsFrom(srcNode);
for (MethodSignature targetNode : outNodes) {
cgEdges.add(new MutablePair<>(srcNode, targetNode));
}
diff --git a/sootup.callgraph/src/main/java/sootup/callgraph/ClassHierarchyAnalysisAlgorithm.java b/sootup.callgraph/src/main/java/sootup/callgraph/ClassHierarchyAnalysisAlgorithm.java
index 43ae55c3f5f..c0f0b633bd5 100644
--- a/sootup.callgraph/src/main/java/sootup/callgraph/ClassHierarchyAnalysisAlgorithm.java
+++ b/sootup.callgraph/src/main/java/sootup/callgraph/ClassHierarchyAnalysisAlgorithm.java
@@ -30,6 +30,7 @@
import sootup.core.jimple.common.expr.JDynamicInvokeExpr;
import sootup.core.jimple.common.expr.JInterfaceInvokeExpr;
import sootup.core.jimple.common.expr.JSpecialInvokeExpr;
+import sootup.core.jimple.common.stmt.InvokableStmt;
import sootup.core.model.MethodModifier;
import sootup.core.model.SootClass;
import sootup.core.model.SootMethod;
@@ -71,13 +72,18 @@ public CallGraph initialize(@Nonnull List entryPoints) {
* called in the invoke expression.
*
* @param method the method object that contains the given invoke expression in the body.
- * @param invokeExpr it contains the call which is resolved.
+ * @param invokableStmt it contains the call which is resolved.
* @return a stream containing all reachable method signatures after applying the CHA call graph
* algorithm
*/
@Override
@Nonnull
- protected Stream resolveCall(SootMethod method, AbstractInvokeExpr invokeExpr) {
+ protected Stream resolveCall(SootMethod method, InvokableStmt invokableStmt) {
+ Optional optInvokeExpr = invokableStmt.getInvokeExpr();
+ if (!optInvokeExpr.isPresent()) {
+ return Stream.empty();
+ }
+ AbstractInvokeExpr invokeExpr = optInvokeExpr.get();
MethodSignature targetMethodSignature = invokeExpr.getMethodSignature();
if ((invokeExpr instanceof JDynamicInvokeExpr)) {
return Stream.empty();
diff --git a/sootup.callgraph/src/main/java/sootup/callgraph/GraphBasedCallGraph.java b/sootup.callgraph/src/main/java/sootup/callgraph/GraphBasedCallGraph.java
index e2470c802a0..bae6cf9ce6c 100644
--- a/sootup.callgraph/src/main/java/sootup/callgraph/GraphBasedCallGraph.java
+++ b/sootup.callgraph/src/main/java/sootup/callgraph/GraphBasedCallGraph.java
@@ -23,13 +23,16 @@
*/
import com.google.common.base.Preconditions;
+import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import javax.annotation.Nonnull;
-import org.jgrapht.graph.DefaultDirectedGraph;
+import org.jgrapht.graph.DirectedPseudograph;
+import sootup.core.jimple.common.stmt.InvokableStmt;
import sootup.core.signatures.MethodSignature;
import sootup.core.signatures.SootClassMemberSignature;
@@ -53,23 +56,22 @@ protected MethodSignature getMethodSignature() {
}
}
- /** This internal class is used to describe the edge in the graph. */
- protected static class Edge {}
-
- @Nonnull private final DefaultDirectedGraph graph;
+ @Nonnull private final DirectedPseudograph graph;
@Nonnull private final Map signatureToVertex;
+ @Nonnull private final List entryMethods;
/** The constructor of the graph based call graph. it initializes the call graph object. */
- public GraphBasedCallGraph() {
- graph = new DefaultDirectedGraph<>(null, null, false);
- signatureToVertex = new HashMap<>();
+ public GraphBasedCallGraph(List entryMethods) {
+ this(new DirectedPseudograph<>(null, null, false), new HashMap<>(), entryMethods);
}
- public GraphBasedCallGraph(
- @Nonnull DefaultDirectedGraph graph,
- @Nonnull Map signatureToVertex) {
+ protected GraphBasedCallGraph(
+ @Nonnull DirectedPseudograph graph,
+ @Nonnull Map signatureToVertex,
+ @Nonnull List entryMethods) {
this.graph = graph;
this.signatureToVertex = signatureToVertex;
+ this.entryMethods = entryMethods;
}
@Override
@@ -79,19 +81,31 @@ public void addMethod(@Nonnull MethodSignature calledMethod) {
}
protected void addMethod(@Nonnull MethodSignature calledMethod, Vertex vertex) {
+ if (containsMethod(calledMethod)) {
+ return;
+ }
graph.addVertex(vertex);
signatureToVertex.put(calledMethod, vertex);
}
@Override
public void addCall(
- @Nonnull MethodSignature sourceMethod, @Nonnull MethodSignature targetMethod) {
- addCall(sourceMethod, targetMethod, new Edge());
+ @Nonnull MethodSignature sourceMethod,
+ @Nonnull MethodSignature targetMethod,
+ @Nonnull InvokableStmt invokableStmt) {
+ addCall(sourceMethod, targetMethod, new Call(sourceMethod, targetMethod, invokableStmt));
}
protected void addCall(
- @Nonnull MethodSignature sourceMethod, @Nonnull MethodSignature targetMethod, Edge edge) {
- graph.addEdge(vertexOf(sourceMethod), vertexOf(targetMethod), edge);
+ @Nonnull MethodSignature sourceMethod, @Nonnull MethodSignature targetMethod, Call call) {
+ if (!call.getSourceMethodSignature().equals(sourceMethod)
+ || !call.getTargetMethodSignature().equals(targetMethod)
+ || containsCall(call)) {
+ return;
+ }
+ Vertex source = vertexOf(sourceMethod);
+ Vertex target = vertexOf(targetMethod);
+ graph.addEdge(source, target, call);
}
@Nonnull
@@ -102,7 +116,7 @@ public Set getMethodSignatures() {
@Nonnull
@Override
- public Set callsFrom(@Nonnull MethodSignature sourceMethod) {
+ public Set callTargetsFrom(@Nonnull MethodSignature sourceMethod) {
return graph.outgoingEdgesOf(vertexOf(sourceMethod)).stream()
.map(graph::getEdgeTarget)
.map(targetVertex -> targetVertex.methodSignature)
@@ -111,13 +125,25 @@ public Set callsFrom(@Nonnull MethodSignature sourceMethod) {
@Nonnull
@Override
- public Set callsTo(@Nonnull MethodSignature targetMethod) {
+ public Set callSourcesTo(@Nonnull MethodSignature targetMethod) {
return graph.incomingEdgesOf(vertexOf(targetMethod)).stream()
.map(graph::getEdgeSource)
.map(targetVertex -> targetVertex.methodSignature)
.collect(Collectors.toSet());
}
+ @Nonnull
+ @Override
+ public Set callsFrom(@Nonnull MethodSignature sourceMethod) {
+ return graph.outgoingEdgesOf(vertexOf(sourceMethod));
+ }
+
+ @Nonnull
+ @Override
+ public Set callsTo(@Nonnull MethodSignature targetMethod) {
+ return graph.incomingEdgesOf(vertexOf(targetMethod));
+ }
+
@Override
public boolean containsMethod(@Nonnull MethodSignature method) {
return signatureToVertex.containsKey(method);
@@ -125,11 +151,18 @@ public boolean containsMethod(@Nonnull MethodSignature method) {
@Override
public boolean containsCall(
- @Nonnull MethodSignature sourceMethod, @Nonnull MethodSignature targetMethod) {
+ @Nonnull MethodSignature sourceMethod,
+ @Nonnull MethodSignature targetMethod,
+ @Nonnull InvokableStmt invokableStmt) {
if (!containsMethod(sourceMethod) || !containsMethod(targetMethod)) {
return false;
}
- return graph.containsEdge(vertexOf(sourceMethod), vertexOf(targetMethod));
+ return containsCall(new Call(sourceMethod, targetMethod, invokableStmt));
+ }
+
+ @Override
+ public boolean containsCall(@Nonnull Call call) {
+ return graph.containsEdge(call);
}
@Override
@@ -146,33 +179,33 @@ public String exportAsDot() {
graph.edgeSet().stream()
.sorted(
Comparator.comparing(
- (Edge edge) -> {
- Vertex edgeSource = graph.getEdgeSource(edge);
+ (Call call) -> {
+ Vertex edgeSource = graph.getEdgeSource(call);
return edgeSource.methodSignature.getDeclClassType().getFullyQualifiedName();
})
.thenComparing(
- (Edge edge) -> {
- Vertex edgeSource = graph.getEdgeSource(edge);
+ (Call call) -> {
+ Vertex edgeSource = graph.getEdgeSource(call);
return edgeSource.methodSignature.getName();
})
.thenComparing(
- (Edge edge) -> {
- Vertex edgeSource = graph.getEdgeSource(edge);
+ (Call call) -> {
+ Vertex edgeSource = graph.getEdgeSource(call);
return edgeSource.methodSignature.getParameterTypes().toString();
})
.thenComparing(
- (Edge edge) -> {
- Vertex edgeTarget = graph.getEdgeTarget(edge);
+ (Call call) -> {
+ Vertex edgeTarget = graph.getEdgeTarget(call);
return edgeTarget.methodSignature.getDeclClassType().getClassName();
})
.thenComparing(
- (Edge edge) -> {
- Vertex edgeTarget = graph.getEdgeTarget(edge);
+ (Call call) -> {
+ Vertex edgeTarget = graph.getEdgeTarget(call);
return edgeTarget.methodSignature.getName();
})
.thenComparing(
- (Edge edge) -> {
- Vertex edgeTarget = graph.getEdgeTarget(edge);
+ (Call call) -> {
+ Vertex edgeTarget = graph.getEdgeTarget(call);
return edgeTarget.methodSignature.getParameterTypes().toString();
}))
.forEach(
@@ -181,9 +214,13 @@ public String exportAsDot() {
Vertex targetVertex = graph.getEdgeTarget(edge);
dotFormatBuilder
.append("\t")
- .append("\"" + sourceVertex.methodSignature + "\"")
+ .append("\"")
+ .append(sourceVertex.methodSignature)
+ .append("\"")
.append(" -> ")
- .append("\"" + targetVertex.methodSignature + "\"")
+ .append("\"")
+ .append(targetVertex.methodSignature)
+ .append("\"")
.append(";\n");
});
@@ -195,7 +232,9 @@ public String exportAsDot() {
@Override
public MutableCallGraph copy() {
return new GraphBasedCallGraph(
- (DefaultDirectedGraph) graph.clone(), new HashMap<>(signatureToVertex));
+ (DirectedPseudograph) graph.clone(),
+ new HashMap<>(signatureToVertex),
+ new ArrayList<>(entryMethods));
}
@Nonnull
@@ -217,21 +256,6 @@ protected Vertex vertexOf(@Nonnull MethodSignature method) {
return methodVertex;
}
- @Nonnull
- protected DefaultDirectedGraph getGraph() {
- return graph;
- }
-
- @Nonnull
- protected Map getSignatureToVertex() {
- return signatureToVertex;
- }
-
- @Nonnull
- protected MethodSignature vertex2MethodSignature(@Nonnull Vertex vertex) {
- return vertex.getMethodSignature();
- }
-
/**
* This method exports the call graph in a human-readable string. The String lists all nodes in
* the call graph. For each node it also lists the outgoing and incoming edges. An outgoing edge
@@ -256,13 +280,13 @@ public String toString() {
.forEach(
method -> {
stringBuilder.append(method).append(":\n");
- callsFrom(method).stream()
+ callTargetsFrom(method).stream()
.sorted(
Comparator.comparing((MethodSignature o) -> o.getDeclClassType().toString())
.thenComparing(SootClassMemberSignature::getName)
.thenComparing(o -> o.getParameterTypes().toString()))
.forEach(m -> stringBuilder.append("\tto ").append(m).append("\n"));
- callsTo(method).stream()
+ callSourcesTo(method).stream()
.sorted(
Comparator.comparing((MethodSignature o) -> o.getDeclClassType().toString())
.thenComparing(SootClassMemberSignature::getName)
@@ -273,4 +297,10 @@ public String toString() {
}
return stringBuilder.toString();
}
+
+ @Override
+ @Nonnull
+ public List getEntryMethods() {
+ return entryMethods;
+ }
}
diff --git a/sootup.callgraph/src/main/java/sootup/callgraph/MutableCallGraph.java b/sootup.callgraph/src/main/java/sootup/callgraph/MutableCallGraph.java
index 57028c90107..6df01fba4ca 100644
--- a/sootup.callgraph/src/main/java/sootup/callgraph/MutableCallGraph.java
+++ b/sootup.callgraph/src/main/java/sootup/callgraph/MutableCallGraph.java
@@ -23,6 +23,7 @@
*/
import javax.annotation.Nonnull;
+import sootup.core.jimple.common.stmt.InvokableStmt;
import sootup.core.signatures.MethodSignature;
/**
@@ -43,6 +44,10 @@ public interface MutableCallGraph extends CallGraph {
*
* @param sourceMethod this parameter defines the source node of the edge in the call graph.
* @param targetMethod this paramter defines the target node of the edge in the call graph.
+ * @param invokableStmt this paramter defines the invoke statement of the edge in the call graph.
*/
- void addCall(@Nonnull MethodSignature sourceMethod, @Nonnull MethodSignature targetMethod);
+ void addCall(
+ @Nonnull MethodSignature sourceMethod,
+ @Nonnull MethodSignature targetMethod,
+ @Nonnull InvokableStmt invokableStmt);
}
diff --git a/sootup.callgraph/src/main/java/sootup/callgraph/RapidTypeAnalysisAlgorithm.java b/sootup.callgraph/src/main/java/sootup/callgraph/RapidTypeAnalysisAlgorithm.java
index 86025307594..d8343dbf0d2 100644
--- a/sootup.callgraph/src/main/java/sootup/callgraph/RapidTypeAnalysisAlgorithm.java
+++ b/sootup.callgraph/src/main/java/sootup/callgraph/RapidTypeAnalysisAlgorithm.java
@@ -26,9 +26,11 @@
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.annotation.Nonnull;
+import sootup.callgraph.CallGraph.Call;
import sootup.core.jimple.common.expr.AbstractInvokeExpr;
import sootup.core.jimple.common.expr.JNewExpr;
import sootup.core.jimple.common.expr.JSpecialInvokeExpr;
+import sootup.core.jimple.common.stmt.InvokableStmt;
import sootup.core.jimple.common.stmt.JAssignStmt;
import sootup.core.model.MethodModifier;
import sootup.core.model.SootMethod;
@@ -46,21 +48,6 @@
*/
public class RapidTypeAnalysisAlgorithm extends AbstractCallGraphAlgorithm {
- /**
- * This private class is used to save reachable calls. Because every method is only processed
- * once, ignored calls are saved to include them at a later time if their class is instantiated at
- * a later time.
- */
- protected static class Call {
- @Nonnull final MethodSignature source;
- @Nonnull final MethodSignature target;
-
- protected Call(@Nonnull MethodSignature source, @Nonnull MethodSignature target) {
- this.source = source;
- this.target = target;
- }
- }
-
@Nonnull protected Set instantiatedClasses = Collections.emptySet();
@Nonnull protected Map> ignoredCalls = Collections.emptyMap();
@@ -128,14 +115,19 @@ protected List collectInstantiatedClassesInMethod(SootMethod method)
* expression.
*
* @param sourceMethod the method object that contains the given invoke expression in the body.
- * @param invokeExpr it contains the call which is resolved.
+ * @param invokableStmt the statement containing the call which is resolved.
* @return a stream containing all reachable method signatures after applying the RTA call graph
* algorithm
*/
@Override
@Nonnull
protected Stream resolveCall(
- SootMethod sourceMethod, AbstractInvokeExpr invokeExpr) {
+ SootMethod sourceMethod, InvokableStmt invokableStmt) {
+ Optional optInvokeExpr = invokableStmt.getInvokeExpr();
+ if (!optInvokeExpr.isPresent()) {
+ return Stream.empty();
+ }
+ AbstractInvokeExpr invokeExpr = optInvokeExpr.get();
MethodSignature resolveBaseMethodSignature = invokeExpr.getMethodSignature();
Stream result = Stream.of(resolveBaseMethodSignature);
@@ -151,10 +143,12 @@ protected Stream resolveCall(
if (instantiatedClasses.contains(resolveBaseMethodSignature.getDeclClassType())) {
return Stream.concat(
Stream.of(concreteBaseMethod.getSignature()),
- resolveAllCallTargets(sourceMethod.getSignature(), resolveBaseMethodSignature));
+ resolveAllCallTargets(
+ sourceMethod.getSignature(), resolveBaseMethodSignature, invokableStmt));
} else {
- saveIgnoredCall(sourceMethod.getSignature(), resolveBaseMethodSignature);
- return resolveAllCallTargets(sourceMethod.getSignature(), resolveBaseMethodSignature);
+ saveIgnoredCall(sourceMethod.getSignature(), resolveBaseMethodSignature, invokableStmt);
+ return resolveAllCallTargets(
+ sourceMethod.getSignature(), resolveBaseMethodSignature, invokableStmt);
}
}
}
@@ -167,11 +161,14 @@ protected Stream resolveCall(
* @param source the method which contains call
* @param resolveBaseMethodSignature the base of the resolving. All subtypes of the declaring
* class are analyzed as potential targets
+ * @param invokableStmt the statement causing the call
* @return a stream of all method signatures of instantiated classes that can be resolved as
* target from the given base method signature.
*/
private Stream resolveAllCallTargets(
- MethodSignature source, MethodSignature resolveBaseMethodSignature) {
+ MethodSignature source,
+ MethodSignature resolveBaseMethodSignature,
+ InvokableStmt invokableStmt) {
return view.getTypeHierarchy()
.subtypesOf(resolveBaseMethodSignature.getDeclClassType())
.map(
@@ -182,7 +179,7 @@ private Stream resolveAllCallTargets(
if (instantiatedClasses.contains(classType)) {
return resolveConcreteDispatch(view, method);
} else {
- saveIgnoredCall(source, method);
+ saveIgnoredCall(source, method, invokableStmt);
return Optional.empty();
}
})
@@ -196,11 +193,13 @@ private Stream resolveAllCallTargets(
*
* @param source the source method of the call
* @param target the target method of the call
+ * @param invokableStmt the statement causing the call
*/
- private void saveIgnoredCall(MethodSignature source, MethodSignature target) {
+ private void saveIgnoredCall(
+ MethodSignature source, MethodSignature target, InvokableStmt invokableStmt) {
ClassType notInstantiatedClass = target.getDeclClassType();
List calls = ignoredCalls.get(notInstantiatedClass);
- Call ignoredCall = new Call(source, target);
+ Call ignoredCall = new Call(source, target, invokableStmt);
if (calls == null) {
calls = new ArrayList<>();
ignoredCalls.put(notInstantiatedClass, calls);
@@ -254,19 +253,16 @@ protected void includeIgnoredCallsToClass(
newEdges.forEach(
call -> {
MethodSignature concreteTarget =
- resolveConcreteDispatch(view, call.target).orElse(null);
+ resolveConcreteDispatch(view, call.getTargetMethodSignature()).orElse(null);
if (concreteTarget == null) {
return;
}
- if (cg.containsMethod(concreteTarget)) {
- // method is already analyzed or is in the work list, simply add the call
- cg.addCall(call.source, concreteTarget);
- } else {
- // new target method found that has to be analyzed
- cg.addMethod(concreteTarget);
- cg.addCall(call.source, concreteTarget);
- workList.push(concreteTarget);
- }
+ addCallToCG(
+ call.getSourceMethodSignature(),
+ concreteTarget,
+ call.getInvokableStmt(),
+ cg,
+ workList);
});
// can be removed because the instantiated class will be considered in future resolves
ignoredCalls.remove(classType);
diff --git a/sootup.callgraph/src/test/java/sootup/callgraph/CallGraphTestBase.java b/sootup.callgraph/src/test/java/sootup/callgraph/CallGraphTestBase.java
index fd62eead256..f7fbfafbbdf 100644
--- a/sootup.callgraph/src/test/java/sootup/callgraph/CallGraphTestBase.java
+++ b/sootup.callgraph/src/test/java/sootup/callgraph/CallGraphTestBase.java
@@ -7,10 +7,19 @@
import java.util.List;
import org.junit.jupiter.api.Test;
import sootup.core.inputlocation.AnalysisInputLocation;
+import sootup.core.jimple.basic.Value;
+import sootup.core.jimple.common.expr.JNewArrayExpr;
+import sootup.core.jimple.common.expr.JNewExpr;
+import sootup.core.jimple.common.expr.JNewMultiArrayExpr;
+import sootup.core.jimple.common.ref.JFieldRef;
+import sootup.core.jimple.common.stmt.InvokableStmt;
+import sootup.core.jimple.common.stmt.JAssignStmt;
+import sootup.core.jimple.common.stmt.Stmt;
import sootup.core.model.SootClass;
import sootup.core.model.SootMethod;
import sootup.core.model.SourceType;
import sootup.core.signatures.MethodSignature;
+import sootup.core.types.ClassType;
import sootup.java.bytecode.inputlocation.DefaultRTJarAnalysisInputLocation;
import sootup.java.bytecode.inputlocation.JavaClassPathAnalysisInputLocation;
import sootup.java.core.JavaIdentifierFactory;
@@ -24,6 +33,7 @@ public abstract class CallGraphTestBase {
protected JavaIdentifierFactory identifierFactory = JavaIdentifierFactory.getInstance();
protected JavaClassType mainClassSignature;
protected MethodSignature mainMethodSignature;
+ protected JavaView view;
protected abstract T createAlgorithm(JavaView view);
@@ -62,7 +72,7 @@ CallGraph loadCallGraph(String testDirectory, boolean useSourceCodeFrontend, Str
+ (useSourceCodeFrontend ? "source" : "binary");
// JavaView view = viewToClassPath.computeIfAbsent(classPath, this::createViewForClassPath);
- JavaView view = createViewForClassPath(classPath, useSourceCodeFrontend);
+ view = createViewForClassPath(classPath, useSourceCodeFrontend);
mainClassSignature = identifierFactory.getClassType(className);
mainMethodSignature =
@@ -83,6 +93,88 @@ CallGraph loadCallGraph(String testDirectory, boolean useSourceCodeFrontend, Str
return cg;
}
+ protected InvokableStmt getInvokableStmt(
+ MethodSignature sourceMethod, MethodSignature staticTargetMethod) {
+ return getInvokableStmt(sourceMethod, staticTargetMethod, 0);
+ }
+
+ protected InvokableStmt getInvokableStmt(
+ MethodSignature sourceMethod, MethodSignature staticTargetMethod, int index) {
+ int currentIndex = 0;
+ SootMethod method = view.getMethod(sourceMethod).orElse(null);
+ assertNotNull(method);
+ for (Stmt invokableStmt : method.getBody().getStmts()) {
+ if (invokableStmt instanceof InvokableStmt
+ && ((InvokableStmt) invokableStmt).containsInvokeExpr()
+ && ((InvokableStmt) invokableStmt)
+ .getInvokeExpr()
+ .get()
+ .getMethodSignature()
+ .equals(staticTargetMethod)) {
+ if (currentIndex == index) {
+ return (InvokableStmt) invokableStmt;
+ }
+ currentIndex++;
+ }
+ }
+ throw new RuntimeException(
+ "No invokable stmt of method " + staticTargetMethod + " found for " + sourceMethod);
+ }
+
+ protected InvokableStmt getInvokableStmtNonInvokeExpr(
+ MethodSignature sourceMethod, ClassType targetClass) {
+ return getInvokableStmtNonInvokeExpr(sourceMethod, targetClass, false, 0);
+ }
+
+ protected InvokableStmt getInvokableStmtNonInvokeExpr(
+ MethodSignature sourceMethod, ClassType targetClass, boolean leftExpr, int index) {
+ int currentIndex = 0;
+ SootMethod method = view.getMethod(sourceMethod).orElse(null);
+ assertNotNull(method);
+
+ InstantiateClassValueVisitor instantiateVisitor = new InstantiateClassValueVisitor();
+ for (Stmt invokableStmt : method.getBody().getStmts()) {
+ // look only at assigments which do Invoke but does not contain a direct invoke expr
+ // static fields and new array expressions
+ if (invokableStmt instanceof JAssignStmt
+ && !((InvokableStmt) invokableStmt).containsInvokeExpr()
+ && ((InvokableStmt) invokableStmt).invokesStaticInitializer()) {
+ Value expr;
+ // look at the left or right side of the assigment
+ if (leftExpr) {
+ expr = ((JAssignStmt) invokableStmt).getLeftOp();
+ } else {
+ expr = ((JAssignStmt) invokableStmt).getRightOp();
+ }
+ // extract the class type
+ ClassType classType = null;
+ if (expr instanceof JFieldRef) {
+ classType = ((JFieldRef) expr).getFieldSignature().getDeclClassType();
+ } else if (expr instanceof JNewExpr
+ || expr instanceof JNewArrayExpr
+ || expr instanceof JNewMultiArrayExpr) {
+ instantiateVisitor.init();
+ expr.accept(instantiateVisitor);
+ classType = instantiateVisitor.getResult();
+ }
+ // probably caused by invoke caused on the other operand side
+ if (classType == null) {
+ continue;
+ }
+ if (classType.equals(targetClass)) {
+ // found fitting stmt in given position
+ if (currentIndex == index) {
+ return (InvokableStmt) invokableStmt;
+ }
+ // search next fitting stmt
+ currentIndex++;
+ }
+ }
+ }
+ throw new RuntimeException(
+ "No invokable assignment stmt of class " + targetClass + " found for " + sourceMethod);
+ }
+
@Test
public void testSingleMethod() {
CallGraph cg = loadCallGraph("Misc", "example.SingleMethod");
@@ -131,10 +223,16 @@ public void testRecursiveCall() {
// 2 methods + Object::clinit
assertEquals(3, cg.getMethodSignatures().size());
- assertTrue(cg.containsCall(mainMethodSignature, mainMethodSignature));
- assertTrue(cg.containsCall(mainMethodSignature, method));
- // 2 calls +1 clinit calls
- assertEquals(3, cg.callsFrom(mainMethodSignature).size());
+ assertTrue(
+ cg.containsCall(
+ mainMethodSignature,
+ mainMethodSignature,
+ getInvokableStmt(mainMethodSignature, mainMethodSignature)));
+ assertTrue(
+ cg.containsCall(
+ mainMethodSignature, method, getInvokableStmt(mainMethodSignature, method)));
+ // 2 calls +2 clinit calls
+ assertEquals(4, cg.callsFrom(mainMethodSignature).size());
}
@Test
@@ -143,7 +241,11 @@ public void testConcreteCall() {
MethodSignature targetMethod =
identifierFactory.getMethodSignature(
identifierFactory.getClassType("cvc.Class"), "target", "void", Collections.emptyList());
- assertTrue(cg.containsCall(mainMethodSignature, targetMethod));
+ assertTrue(
+ cg.containsCall(
+ mainMethodSignature,
+ targetMethod,
+ getInvokableStmt(mainMethodSignature, targetMethod)));
}
@Test
@@ -155,7 +257,17 @@ public void testConcreteCallInSuperClass() {
"target",
"void",
Collections.emptyList());
- assertTrue(cg.containsCall(mainMethodSignature, targetMethod));
+ MethodSignature targetMethodInvoked =
+ identifierFactory.getMethodSignature(
+ identifierFactory.getClassType("cvcsc.Class"),
+ "target",
+ "void",
+ Collections.emptyList());
+ assertTrue(
+ cg.containsCall(
+ mainMethodSignature,
+ targetMethod,
+ getInvokableStmt(mainMethodSignature, targetMethodInvoked)));
}
@Test
@@ -173,8 +285,22 @@ public void testConcreteCallDifferentDefaultMethodInSubClass() {
"target",
"void",
Collections.emptyList());
- assertTrue(cg.containsCall(mainMethodSignature, interfaceMethod));
- assertTrue(cg.containsCall(mainMethodSignature, subInterfaceMethod));
+ MethodSignature invokedMethod =
+ identifierFactory.getMethodSignature(
+ identifierFactory.getClassType("cvcscddi.Class"),
+ "target",
+ "void",
+ Collections.emptyList());
+ assertTrue(
+ cg.containsCall(
+ mainMethodSignature,
+ interfaceMethod,
+ getInvokableStmt(mainMethodSignature, invokedMethod)));
+ assertTrue(
+ cg.containsCall(
+ mainMethodSignature,
+ subInterfaceMethod,
+ getInvokableStmt(mainMethodSignature, invokedMethod)));
}
@Test
@@ -186,7 +312,17 @@ public void testConcreteCallInSuperClassWithDefaultInterface() {
"target",
"void",
Collections.emptyList());
- assertTrue(cg.containsCall(mainMethodSignature, targetMethod));
+ MethodSignature targetMethodInvoked =
+ identifierFactory.getMethodSignature(
+ identifierFactory.getClassType("cvcscwi.Class"),
+ "target",
+ "void",
+ Collections.emptyList());
+ assertTrue(
+ cg.containsCall(
+ mainMethodSignature,
+ targetMethod,
+ getInvokableStmt(mainMethodSignature, targetMethodInvoked)));
}
@Test
@@ -198,7 +334,17 @@ public void testConcreteCallInInterface() {
"target",
"void",
Collections.emptyList());
- assertTrue(cg.containsCall(mainMethodSignature, targetMethod));
+ MethodSignature invokedMethod =
+ identifierFactory.getMethodSignature(
+ identifierFactory.getClassType("cvci.Class"),
+ "target",
+ "void",
+ Collections.emptyList());
+ assertTrue(
+ cg.containsCall(
+ mainMethodSignature,
+ targetMethod,
+ getInvokableStmt(mainMethodSignature, invokedMethod)));
}
@Test
@@ -210,7 +356,17 @@ public void testConcreteCallInSubInterface() {
"target",
"void",
Collections.emptyList());
- assertTrue(cg.containsCall(mainMethodSignature, targetMethod));
+ MethodSignature targetMethodInvoked =
+ identifierFactory.getMethodSignature(
+ identifierFactory.getClassType("cvcsi.Class"),
+ "target",
+ "void",
+ Collections.emptyList());
+ assertTrue(
+ cg.containsCall(
+ mainMethodSignature,
+ targetMethod,
+ getInvokableStmt(mainMethodSignature, targetMethodInvoked)));
}
@Test
@@ -222,7 +378,17 @@ public void testConcreteCallInSuperClassSubInterface() {
"target",
"void",
Collections.emptyList());
- assertTrue(cg.containsCall(mainMethodSignature, targetMethod));
+ MethodSignature targetMethodInvoked =
+ identifierFactory.getMethodSignature(
+ identifierFactory.getClassType("cvcscsi.Class"),
+ "target",
+ "void",
+ Collections.emptyList());
+ assertTrue(
+ cg.containsCall(
+ mainMethodSignature,
+ targetMethod,
+ getInvokableStmt(mainMethodSignature, targetMethodInvoked)));
}
@Test
@@ -234,7 +400,7 @@ public void testClinitCallEntryPoint() {
"",
"void",
Collections.emptyList());
- assertTrue(cg.containsCall(mainMethodSignature, targetMethod));
+ assertTrue(cg.getEntryMethods().contains(targetMethod));
}
@Test
@@ -249,7 +415,8 @@ public void testClinitCallProcessed() {
MethodSignature targetMethod =
identifierFactory.getMethodSignature(
identifierFactory.getClassType("ccp.Class"), "method", "int", Collections.emptyList());
- assertTrue(cg.containsCall(sourceMethod, targetMethod));
+ assertTrue(
+ cg.containsCall(sourceMethod, targetMethod, getInvokableStmt(sourceMethod, targetMethod)));
}
@Test
@@ -261,28 +428,53 @@ public void testClinitCallConstructor() {
"",
"void",
Collections.emptyList());
- assertTrue(cg.containsCall(mainMethodSignature, targetMethod));
+
+ assertTrue(
+ cg.containsCall(
+ mainMethodSignature,
+ targetMethod,
+ getInvokableStmtNonInvokeExpr(
+ mainMethodSignature, identifierFactory.getClassType("ccc.DirectType"))));
+
MethodSignature arrayMethod =
identifierFactory.getMethodSignature(
identifierFactory.getClassType("ccc.ArrayType"),
"",
"void",
Collections.emptyList());
- assertTrue(cg.containsCall(mainMethodSignature, arrayMethod));
+ assertTrue(
+ cg.containsCall(
+ mainMethodSignature,
+ arrayMethod,
+ getInvokableStmtNonInvokeExpr(
+ mainMethodSignature, identifierFactory.getClassType("ccc.ArrayType"))));
+
MethodSignature arrayDimMethod =
identifierFactory.getMethodSignature(
identifierFactory.getClassType("ccc.ArrayDimType"),
"",
"void",
Collections.emptyList());
- assertTrue(cg.containsCall(mainMethodSignature, arrayDimMethod));
+
+ assertTrue(
+ cg.containsCall(
+ mainMethodSignature,
+ arrayDimMethod,
+ getInvokableStmtNonInvokeExpr(
+ mainMethodSignature, identifierFactory.getClassType("ccc.ArrayDimType"))));
+
MethodSignature arrayInArrayMethod =
identifierFactory.getMethodSignature(
identifierFactory.getClassType("ccc.ArrayInArrayType"),
"",
"void",
Collections.emptyList());
- assertTrue(cg.containsCall(mainMethodSignature, arrayInArrayMethod));
+ assertTrue(
+ cg.containsCall(
+ mainMethodSignature,
+ arrayInArrayMethod,
+ getInvokableStmtNonInvokeExpr(
+ mainMethodSignature, identifierFactory.getClassType("ccc.ArrayInArrayType"))));
}
@Test
@@ -294,14 +486,17 @@ public void testClinitCallSuperConstructor() {
"",
"void",
Collections.emptyList());
- assertTrue(cg.containsCall(mainMethodSignature, targetMethod));
+ InvokableStmt invokedStmt =
+ getInvokableStmtNonInvokeExpr(
+ mainMethodSignature, identifierFactory.getClassType("ccsc.Clinit"));
+ assertTrue(cg.containsCall(mainMethodSignature, targetMethod, invokedStmt));
MethodSignature targetMethod2 =
identifierFactory.getMethodSignature(
identifierFactory.getClassType("ccsc.SuperClinit"),
"",
"void",
Collections.emptyList());
- assertTrue(cg.containsCall(mainMethodSignature, targetMethod2));
+ assertTrue(cg.containsCall(mainMethodSignature, targetMethod2, invokedStmt));
}
@Test
@@ -313,7 +508,17 @@ public void testClinitStaticMethodCall() {
"",
"void",
Collections.emptyList());
- assertTrue(cg.containsCall(mainMethodSignature, targetMethod));
+ MethodSignature targetMethodInvoked =
+ identifierFactory.getMethodSignature(
+ identifierFactory.getClassType("ccsmc.Clinit"),
+ "method",
+ "void",
+ Collections.emptyList());
+ assertTrue(
+ cg.containsCall(
+ mainMethodSignature,
+ targetMethod,
+ getInvokableStmt(mainMethodSignature, targetMethodInvoked)));
}
@Test
@@ -325,7 +530,11 @@ public void testClinitStaticField() {
"",
"void",
Collections.emptyList());
- assertTrue(cg.containsCall(mainMethodSignature, targetMethod));
+ assertTrue(
+ cg.containsCall(
+ mainMethodSignature,
+ targetMethod,
+ getInvokableStmtNonInvokeExpr(mainMethodSignature, targetMethod.getDeclClassType())));
}
@Test
@@ -334,7 +543,11 @@ public void testNonVirtualCall1() {
MethodSignature targetMethod =
identifierFactory.getMethodSignature(
mainClassSignature, "method", "void", Collections.emptyList());
- assertTrue(cg.containsCall(mainMethodSignature, targetMethod));
+ assertTrue(
+ cg.containsCall(
+ mainMethodSignature,
+ targetMethod,
+ getInvokableStmt(mainMethodSignature, targetMethod)));
}
@Test
@@ -343,7 +556,11 @@ public void testNonVirtualCall2() {
MethodSignature targetMethod =
identifierFactory.getMethodSignature(
mainClassSignature, "", "void", Collections.emptyList());
- assertTrue(cg.containsCall(mainMethodSignature, targetMethod));
+ assertTrue(
+ cg.containsCall(
+ mainMethodSignature,
+ targetMethod,
+ getInvokableStmt(mainMethodSignature, targetMethod)));
}
@Test
@@ -355,7 +572,11 @@ public void testNonVirtualCall3() {
MethodSignature uncalledMethod =
identifierFactory.getMethodSignature(
mainClassSignature, "method", "void", Collections.singletonList("int"));
- assertTrue(cg.containsCall(mainMethodSignature, targetMethod));
+ assertTrue(
+ cg.containsCall(
+ mainMethodSignature,
+ targetMethod,
+ getInvokableStmt(mainMethodSignature, targetMethod)));
assertFalse(cg.containsMethod(uncalledMethod));
}
@@ -365,7 +586,9 @@ public void testNonVirtualCall4() {
MethodSignature firstMethod =
identifierFactory.getMethodSignature(
mainClassSignature, "method", "void", Collections.emptyList());
- assertTrue(cg.containsCall(mainMethodSignature, firstMethod));
+ assertTrue(
+ cg.containsCall(
+ mainMethodSignature, firstMethod, getInvokableStmt(mainMethodSignature, firstMethod)));
MethodSignature targetMethod =
identifierFactory.getMethodSignature(
@@ -373,7 +596,8 @@ public void testNonVirtualCall4() {
"method",
"void",
Collections.emptyList());
- assertTrue(cg.containsCall(firstMethod, targetMethod));
+ assertTrue(
+ cg.containsCall(firstMethod, targetMethod, getInvokableStmt(firstMethod, targetMethod)));
}
@Test
@@ -383,7 +607,9 @@ public void testNonVirtualCall5() {
MethodSignature firstMethod =
identifierFactory.getMethodSignature(
identifierFactory.getClassType("nvc5.Sub"), "method", "void", Collections.emptyList());
- assertTrue(cg.containsCall(mainMethodSignature, firstMethod));
+ assertTrue(
+ cg.containsCall(
+ mainMethodSignature, firstMethod, getInvokableStmt(mainMethodSignature, firstMethod)));
MethodSignature targetMethod =
identifierFactory.getMethodSignature(
@@ -391,7 +617,8 @@ public void testNonVirtualCall5() {
"method",
"void",
Collections.emptyList());
- assertTrue(cg.containsCall(firstMethod, targetMethod));
+ assertTrue(
+ cg.containsCall(firstMethod, targetMethod, getInvokableStmt(firstMethod, targetMethod)));
}
@Test
@@ -401,7 +628,11 @@ public void testVirtualCall1() {
MethodSignature targetMethod =
identifierFactory.getMethodSignature(
mainClassSignature, "target", "void", Collections.emptyList());
- assertTrue(cg.containsCall(mainMethodSignature, targetMethod));
+ assertTrue(
+ cg.containsCall(
+ mainMethodSignature,
+ targetMethod,
+ getInvokableStmt(mainMethodSignature, targetMethod)));
}
@Test
@@ -412,17 +643,28 @@ public void testVirtualCall2() {
MethodSignature constructorMethod =
identifierFactory.getMethodSignature(
subClassSig, "", "void", Collections.emptyList());
- assertTrue(cg.containsCall(mainMethodSignature, constructorMethod));
+ assertTrue(
+ cg.containsCall(
+ mainMethodSignature,
+ constructorMethod,
+ getInvokableStmt(mainMethodSignature, constructorMethod)));
MethodSignature callMethod =
identifierFactory.getMethodSignature(
mainClassSignature, "callMethod", "void", Collections.singletonList("vc2.Class"));
- assertTrue(cg.containsCall(mainMethodSignature, callMethod));
+ assertTrue(
+ cg.containsCall(
+ mainMethodSignature, callMethod, getInvokableStmt(mainMethodSignature, callMethod)));
MethodSignature targetMethod =
identifierFactory.getMethodSignature(
subClassSig, "method", "void", Collections.emptyList());
- assertTrue(cg.containsCall(callMethod, targetMethod));
+ MethodSignature targetMethodInvoked =
+ identifierFactory.getMethodSignature(
+ mainClassSignature, "method", "void", Collections.emptyList());
+ assertTrue(
+ cg.containsCall(
+ callMethod, targetMethod, getInvokableStmt(callMethod, targetMethodInvoked)));
}
@Test
@@ -433,7 +675,11 @@ public void testVirtualCall3() {
MethodSignature constructorMethod =
identifierFactory.getMethodSignature(
subClassSig, "", "void", Collections.emptyList());
- assertTrue(cg.containsCall(mainMethodSignature, constructorMethod));
+ assertTrue(
+ cg.containsCall(
+ mainMethodSignature,
+ constructorMethod,
+ getInvokableStmt(mainMethodSignature, constructorMethod)));
MethodSignature callMethod =
identifierFactory.getMethodSignature(
@@ -441,12 +687,22 @@ public void testVirtualCall3() {
"callOnInterface",
"void",
Collections.singletonList("vc3.Interface"));
- assertTrue(cg.containsCall(mainMethodSignature, callMethod));
+ assertTrue(
+ cg.containsCall(
+ mainMethodSignature, callMethod, getInvokableStmt(mainMethodSignature, callMethod)));
MethodSignature targetMethod =
identifierFactory.getMethodSignature(
subClassSig, "method", "void", Collections.emptyList());
- assertTrue(cg.containsCall(callMethod, targetMethod));
+ MethodSignature targetMethodInvoked =
+ identifierFactory.getMethodSignature(
+ identifierFactory.getClassType("vc3.Interface"),
+ "method",
+ "void",
+ Collections.emptyList());
+ assertTrue(
+ cg.containsCall(
+ callMethod, targetMethod, getInvokableStmt(callMethod, targetMethodInvoked)));
}
@Test
@@ -457,7 +713,17 @@ public void testVirtualCall4() {
MethodSignature callMethod =
identifierFactory.getMethodSignature(
identifierFactory.getClassType("vc4.Class"), "method", "void", Collections.emptyList());
- assertTrue(cg.containsCall(mainMethodSignature, callMethod));
+ MethodSignature callMethodInvoked =
+ identifierFactory.getMethodSignature(
+ identifierFactory.getClassType("vc4.Interface"),
+ "method",
+ "void",
+ Collections.emptyList());
+ assertTrue(
+ cg.containsCall(
+ mainMethodSignature,
+ callMethod,
+ getInvokableStmt(mainMethodSignature, callMethodInvoked)));
}
@Test
@@ -475,8 +741,16 @@ public void testDynamicInterfaceMethod0() {
"method",
"void",
Collections.emptyList());
- assertFalse(cg.containsCall(mainMethodSignature, interfaceMethod));
- assertTrue(cg.containsCall(mainMethodSignature, classMethod));
+ assertFalse(
+ cg.containsCall(
+ mainMethodSignature,
+ interfaceMethod,
+ getInvokableStmt(mainMethodSignature, interfaceMethod)));
+ assertTrue(
+ cg.containsCall(
+ mainMethodSignature,
+ classMethod,
+ getInvokableStmt(mainMethodSignature, interfaceMethod)));
}
@Test
@@ -488,7 +762,9 @@ public void testDynamicInterfaceMethod1() {
"method",
"void",
Collections.emptyList());
- assertTrue(cg.containsCall(mainMethodSignature, callMethod));
+ assertTrue(
+ cg.containsCall(
+ mainMethodSignature, callMethod, getInvokableStmt(mainMethodSignature, callMethod)));
}
@Test
@@ -501,7 +777,15 @@ public void testDynamicInterfaceMethod2() {
"method",
"void",
Collections.emptyList());
- assertTrue(cg.containsCall(mainMethodSignature, callMethod));
+ MethodSignature invokedMethod =
+ identifierFactory.getMethodSignature(
+ identifierFactory.getClassType("j8dim2.Interface"),
+ "method",
+ "void",
+ Collections.emptyList());
+ assertTrue(
+ cg.containsCall(
+ mainMethodSignature, callMethod, getInvokableStmt(mainMethodSignature, invokedMethod)));
}
@Test
@@ -511,7 +795,9 @@ public void testDynamicInterfaceMethod3() {
MethodSignature callMethod =
identifierFactory.getMethodSignature(
mainClassSignature, "method", "void", Collections.emptyList());
- assertTrue(cg.containsCall(mainMethodSignature, callMethod));
+ assertTrue(
+ cg.containsCall(
+ mainMethodSignature, callMethod, getInvokableStmt(mainMethodSignature, callMethod)));
}
@Test
@@ -524,7 +810,15 @@ public void testDynamicInterfaceMethod4() {
"method",
"void",
Collections.emptyList());
- assertTrue(cg.containsCall(mainMethodSignature, callMethod));
+ MethodSignature invokedMethod =
+ identifierFactory.getMethodSignature(
+ identifierFactory.getClassType("j8dim4.SubClass"),
+ "method",
+ "void",
+ Collections.emptyList());
+ assertTrue(
+ cg.containsCall(
+ mainMethodSignature, callMethod, getInvokableStmt(mainMethodSignature, invokedMethod)));
}
@Test
@@ -537,12 +831,28 @@ public void testDynamicInterfaceMethod5() {
"method",
"void",
Collections.emptyList());
- assertTrue(cg.containsCall(mainMethodSignature, method));
+ MethodSignature invokedMethod =
+ identifierFactory.getMethodSignature(
+ identifierFactory.getClassType("j8dim5.Class"),
+ "method",
+ "void",
+ Collections.emptyList());
+ assertTrue(
+ cg.containsCall(
+ mainMethodSignature, method, getInvokableStmt(mainMethodSignature, invokedMethod)));
MethodSignature compute =
identifierFactory.getMethodSignature(
mainClassSignature, "compute", "void", Collections.emptyList());
- assertTrue(cg.containsCall(mainMethodSignature, compute));
+ MethodSignature invokedCompute =
+ identifierFactory.getMethodSignature(
+ identifierFactory.getClassType("j8dim5.Class"),
+ "compute",
+ "void",
+ Collections.emptyList());
+ assertTrue(
+ cg.containsCall(
+ mainMethodSignature, compute, getInvokableStmt(mainMethodSignature, invokedCompute)));
}
@Test
@@ -555,7 +865,17 @@ public void testDynamicInterfaceMethod6() {
"method",
"void",
Collections.emptyList());
- assertTrue(cg.containsCall(mainMethodSignature, combinedInterfaceMethod));
+ MethodSignature combinedInterfaceMethodInvoked =
+ identifierFactory.getMethodSignature(
+ identifierFactory.getClassType("j8dim6.Demo$1"),
+ "method",
+ "void",
+ Collections.emptyList());
+ assertTrue(
+ cg.containsCall(
+ mainMethodSignature,
+ combinedInterfaceMethod,
+ getInvokableStmt(mainMethodSignature, combinedInterfaceMethodInvoked)));
MethodSignature method =
identifierFactory.getMethodSignature(
@@ -563,7 +883,9 @@ public void testDynamicInterfaceMethod6() {
"method",
"void",
Collections.emptyList());
- assertTrue(cg.containsCall(combinedInterfaceMethod, method));
+ assertTrue(
+ cg.containsCall(
+ combinedInterfaceMethod, method, getInvokableStmt(combinedInterfaceMethod, method)));
MethodSignature anotherMethod =
identifierFactory.getMethodSignature(
@@ -571,7 +893,11 @@ public void testDynamicInterfaceMethod6() {
"method",
"void",
Collections.emptyList());
- assertTrue(cg.containsCall(combinedInterfaceMethod, anotherMethod));
+ assertTrue(
+ cg.containsCall(
+ combinedInterfaceMethod,
+ anotherMethod,
+ getInvokableStmt(combinedInterfaceMethod, anotherMethod)));
}
@Test
@@ -585,7 +911,9 @@ public void testStaticInterfaceMethod() {
"void",
Collections.emptyList());
- assertTrue(cg.containsCall(mainMethodSignature, method));
+ assertTrue(
+ cg.containsCall(
+ mainMethodSignature, method, getInvokableStmt(mainMethodSignature, method)));
}
@Test
@@ -603,8 +931,14 @@ public void testAbstractMethod() {
"void",
Collections.emptyList());
- assertTrue(cg.containsCall(mainMethodSignature, method));
- assertFalse(cg.containsCall(mainMethodSignature, abstractMethod));
+ assertTrue(
+ cg.containsCall(
+ mainMethodSignature, method, getInvokableStmt(mainMethodSignature, abstractMethod)));
+ assertFalse(
+ cg.containsCall(
+ mainMethodSignature,
+ abstractMethod,
+ getInvokableStmt(mainMethodSignature, abstractMethod)));
}
@Test
@@ -628,14 +962,28 @@ public void testAbstractMethodInSubClass() {
"void",
Collections.emptyList());
- assertTrue(cg.containsCall(mainMethodSignature, method));
- assertFalse(cg.containsCall(mainMethodSignature, abstractMethod));
+ assertTrue(
+ cg.containsCall(
+ mainMethodSignature, method, getInvokableStmt(mainMethodSignature, superMethod)));
+ assertFalse(
+ cg.containsCall(
+ mainMethodSignature,
+ abstractMethod,
+ getInvokableStmt(mainMethodSignature, superMethod)));
if (this instanceof ClassHierarchyAnalysisAlgorithmTest) {
- assertTrue(cg.containsCall(mainMethodSignature, superMethod));
+ assertTrue(
+ cg.containsCall(
+ mainMethodSignature,
+ superMethod,
+ getInvokableStmt(mainMethodSignature, superMethod)));
}
if (this instanceof RapidTypeAnalysisAlgorithmTest) {
- assertFalse(cg.containsCall(mainMethodSignature, superMethod));
+ assertFalse(
+ cg.containsCall(
+ mainMethodSignature,
+ superMethod,
+ getInvokableStmt(mainMethodSignature, superMethod)));
}
}
@@ -647,6 +995,13 @@ public void testAbstractMethodMissingMethodInSuperclass() {
identifierFactory.getMethodSignature(
identifierFactory.getClassType("am3.Class"), "method", "void", Collections.emptyList());
+ MethodSignature superMethod =
+ identifierFactory.getMethodSignature(
+ identifierFactory.getClassType("am3.SuperClass"),
+ "method",
+ "void",
+ Collections.emptyList());
+
MethodSignature abstractMethod =
identifierFactory.getMethodSignature(
identifierFactory.getClassType("am3.AbstractClass"),
@@ -654,8 +1009,14 @@ public void testAbstractMethodMissingMethodInSuperclass() {
"void",
Collections.emptyList());
- assertTrue(cg.containsCall(mainMethodSignature, method));
- assertFalse(cg.containsCall(mainMethodSignature, abstractMethod));
+ assertTrue(
+ cg.containsCall(
+ mainMethodSignature, method, getInvokableStmt(mainMethodSignature, superMethod)));
+ assertFalse(
+ cg.containsCall(
+ mainMethodSignature,
+ abstractMethod,
+ getInvokableStmt(mainMethodSignature, superMethod)));
}
@Test
@@ -706,7 +1067,6 @@ public void testNoMainMethod() {
algorithm.initialize();
fail("Runtime Exception not thrown, when no main methods are defined.");
} catch (RuntimeException e) {
- System.out.println(e.getMessage());
assertEquals(
e.getMessage(),
"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.");
@@ -749,4 +1109,145 @@ public void testStopAtLibraryClass() {
}
}
}
+
+ @Test
+ public void testMultiCallsToSameTarget() {
+ CallGraph cg = loadCallGraph("Misc", "multi.MultipleCallsToSameTarget");
+
+ MethodSignature constructorMethod =
+ identifierFactory.getMethodSignature(
+ identifierFactory.getClassType("multi.Instantiated"),
+ "",
+ "void",
+ Collections.emptyList());
+
+ MethodSignature virtualMethod =
+ identifierFactory.getMethodSignature(
+ identifierFactory.getClassType("multi.Instantiated"),
+ "method",
+ "void",
+ Collections.emptyList());
+
+ MethodSignature staticMethod =
+ identifierFactory.getMethodSignature(
+ identifierFactory.getClassType("multi.MultiCalls"),
+ "method",
+ "void",
+ Collections.emptyList());
+
+ MethodSignature staticMethodField =
+ identifierFactory.getMethodSignature(
+ identifierFactory.getClassType("multi.FieldLeft"),
+ "method",
+ "void",
+ Collections.emptyList());
+
+ assertTrue(
+ cg.containsCall(
+ mainMethodSignature,
+ constructorMethod,
+ getInvokableStmt(mainMethodSignature, constructorMethod, 0)));
+ assertTrue(
+ cg.containsCall(
+ mainMethodSignature,
+ constructorMethod,
+ getInvokableStmt(mainMethodSignature, constructorMethod, 1)));
+
+ assertTrue(
+ cg.containsCall(
+ mainMethodSignature,
+ staticMethod,
+ getInvokableStmt(mainMethodSignature, staticMethod, 0)));
+ assertTrue(
+ cg.containsCall(
+ mainMethodSignature,
+ staticMethod,
+ getInvokableStmt(mainMethodSignature, staticMethod, 1)));
+
+ assertTrue(
+ cg.containsCall(
+ mainMethodSignature,
+ staticMethodField,
+ getInvokableStmt(mainMethodSignature, staticMethodField, 0)));
+
+ assertTrue(
+ cg.containsCall(
+ mainMethodSignature,
+ virtualMethod,
+ getInvokableStmt(mainMethodSignature, virtualMethod, 0)));
+
+ assertTrue(
+ cg.containsCall(
+ mainMethodSignature,
+ virtualMethod,
+ getInvokableStmt(mainMethodSignature, virtualMethod, 1)));
+
+ checkClinit(cg, "multi.Instantiated", 0, false, null);
+ checkClinit(cg, "multi.Instantiated", 1, false, null);
+
+ checkClinit(cg, "multi.FieldLeft", 0, true, null);
+ checkClinit(cg, "multi.FieldLeft", 1, true, null);
+ checkClinit(cg, "multi.FieldLeft", 2, true, null);
+
+ checkClinit(cg, "multi.FieldRight", 0, false, null);
+ checkClinit(cg, "multi.FieldRight", 1, false, null);
+
+ checkClinit(cg, "multi.MultiCalls", 0, false, staticMethod);
+ checkClinit(cg, "multi.MultiCalls", 1, false, staticMethod);
+
+ assertEquals(27, cg.callsFrom(mainMethodSignature).size());
+
+ assertEquals(2, cg.callsTo(staticMethod).size());
+ assertEquals(1, cg.callsTo(staticMethodField).size());
+ assertEquals(2, cg.callsTo(constructorMethod).size());
+ assertEquals(2, cg.callsTo(virtualMethod).size());
+
+ assertEquals(0, cg.callsFrom(staticMethod).size());
+ assertEquals(0, cg.callsFrom(staticMethodField).size());
+ assertEquals(1, cg.callsFrom(constructorMethod).size());
+ assertEquals(0, cg.callsFrom(virtualMethod).size());
+ }
+
+ private void checkClinit(
+ CallGraph cg, String clinitClassName, int index, boolean left, MethodSignature ms) {
+ ClassType clinitClass = identifierFactory.getClassType(clinitClassName);
+ MethodSignature clinitMethod =
+ identifierFactory.getMethodSignature(
+ clinitClass, "", "void", Collections.emptyList());
+ MethodSignature clinitObject =
+ identifierFactory.getMethodSignature(
+ identifierFactory.getClassType("java.lang.Object"),
+ "",
+ "void",
+ Collections.emptyList());
+ InvokableStmt invokeStmt;
+ if (ms == null) {
+ invokeStmt = getInvokableStmtNonInvokeExpr(mainMethodSignature, clinitClass, left, index);
+ } else {
+ invokeStmt = getInvokableStmt(mainMethodSignature, ms, index);
+ }
+ assertTrue(cg.containsCall(mainMethodSignature, clinitObject, invokeStmt));
+ assertTrue(cg.containsCall(mainMethodSignature, clinitMethod, invokeStmt));
+ }
+
+ @Test
+ public void testIssue903() {
+ CallGraph cg = loadCallGraph("Bugfixes", false, "issue903.B");
+
+ MethodSignature closingCall =
+ identifierFactory.getMethodSignature(
+ identifierFactory.getClassType("issue903.A"), "close", "void", Collections.emptyList());
+ MethodSignature closingCallInvoked =
+ identifierFactory.getMethodSignature(
+ identifierFactory.getClassType("issue903.B"), "close", "void", Collections.emptyList());
+
+ assertTrue(
+ cg.containsCall(
+ mainMethodSignature,
+ closingCall,
+ getInvokableStmt(mainMethodSignature, closingCallInvoked, 0)));
+
+ assertTrue(
+ cg.containsCall(closingCall, closingCall, getInvokableStmt(closingCall, closingCall, 0)));
+ }
}
diff --git a/sootup.callgraph/src/test/java/sootup/callgraph/ClassHierarchyAnalysisAlgorithmTest.java b/sootup.callgraph/src/test/java/sootup/callgraph/ClassHierarchyAnalysisAlgorithmTest.java
index 7fa2445c802..27216c45851 100644
--- a/sootup.callgraph/src/test/java/sootup/callgraph/ClassHierarchyAnalysisAlgorithmTest.java
+++ b/sootup.callgraph/src/test/java/sootup/callgraph/ClassHierarchyAnalysisAlgorithmTest.java
@@ -148,27 +148,91 @@ public void testMiscExample1() {
"void",
Collections.emptyList());
- assertFalse(cg.containsCall(mainMethodSignature, constructorA));
- assertTrue(cg.containsCall(mainMethodSignature, constructorB));
- assertTrue(cg.containsCall(mainMethodSignature, constructorC));
- assertFalse(cg.containsCall(mainMethodSignature, constructorD));
- assertTrue(cg.containsCall(mainMethodSignature, constructorE));
+ assertFalse(
+ cg.containsCall(
+ mainMethodSignature,
+ constructorA,
+ getInvokableStmt(mainMethodSignature, constructorB)));
+ assertTrue(
+ cg.containsCall(
+ mainMethodSignature,
+ constructorB,
+ getInvokableStmt(mainMethodSignature, constructorB)));
+ assertTrue(
+ cg.containsCall(
+ mainMethodSignature,
+ constructorC,
+ getInvokableStmt(mainMethodSignature, constructorC)));
+
+ assertFalse(
+ cg.containsCall(
+ mainMethodSignature,
+ constructorD,
+ getInvokableStmt(mainMethodSignature, constructorC)));
+
+ assertTrue(
+ cg.containsCall(
+ mainMethodSignature,
+ constructorE,
+ getInvokableStmt(mainMethodSignature, constructorE)));
assertFalse(cg.containsMethod(staticMethodA));
- assertTrue(cg.containsCall(mainMethodSignature, staticMethodB));
+ assertTrue(
+ cg.containsCall(
+ mainMethodSignature,
+ staticMethodB,
+ getInvokableStmt(mainMethodSignature, staticMethodB)));
assertFalse(cg.containsMethod(staticMethodC));
assertFalse(cg.containsMethod(staticMethodD));
assertFalse(cg.containsMethod(staticMethodE));
- assertTrue(cg.containsCall(mainMethodSignature, virtualMethodA));
- assertTrue(cg.containsCall(mainMethodSignature, virtualMethodB));
+ assertTrue(
+ cg.containsCall(
+ mainMethodSignature,
+ virtualMethodA,
+ getInvokableStmt(mainMethodSignature, virtualMethodA)));
+ assertTrue(
+ cg.containsCall(
+ mainMethodSignature,
+ virtualMethodB,
+ getInvokableStmt(mainMethodSignature, virtualMethodA)));
assertFalse(cg.containsMethod(virtualMethodC));
- assertTrue(cg.containsCall(mainMethodSignature, virtualMethodD));
- assertTrue(cg.containsCall(mainMethodSignature, virtualMethodE));
-
- assertTrue(cg.containsCall(mainMethodSignature, clinitObject));
-
- assertEquals(9, cg.callsFrom(mainMethodSignature).size());
+ assertTrue(
+ cg.containsCall(
+ mainMethodSignature,
+ virtualMethodD,
+ getInvokableStmt(mainMethodSignature, virtualMethodA)));
+ assertTrue(
+ cg.containsCall(
+ mainMethodSignature,
+ virtualMethodE,
+ getInvokableStmt(mainMethodSignature, virtualMethodA)));
+
+ assertTrue(
+ cg.containsCall(
+ mainMethodSignature,
+ clinitObject,
+ getInvokableStmtNonInvokeExpr(mainMethodSignature, constructorB.getDeclClassType())));
+
+ assertTrue(
+ cg.containsCall(
+ mainMethodSignature,
+ clinitObject,
+ getInvokableStmtNonInvokeExpr(mainMethodSignature, constructorC.getDeclClassType())));
+
+ assertTrue(
+ cg.containsCall(
+ mainMethodSignature,
+ clinitObject,
+ getInvokableStmtNonInvokeExpr(mainMethodSignature, constructorE.getDeclClassType())));
+
+ assertTrue(
+ cg.containsCall(
+ mainMethodSignature,
+ clinitObject,
+ getInvokableStmt(mainMethodSignature, staticMethodB)));
+
+ assertEquals(12, cg.callsFrom(mainMethodSignature).size());
assertEquals(1, cg.callsTo(constructorB).size());
assertEquals(1, cg.callsTo(constructorC).size());
@@ -184,50 +248,5 @@ public void testMiscExample1() {
assertEquals(0, cg.callsFrom(virtualMethodB).size());
assertEquals(0, cg.callsFrom(virtualMethodD).size());
assertEquals(0, cg.callsFrom(virtualMethodE).size());
-
- assertEquals(
- cg.toString().replace("\n", "").replace("\t", ""),
- "GraphBasedCallGraph(14):"
- + "()>:"
- + "to ()>"
- + "from ()>"
- + "from ()>"
- + "from ()>"
- + ":"
- + "from "
- + "()>:"
- + "to ()>"
- + "from "
- + ":"
- + "from "
- + ":"
- + "from "
- + "()>:"
- + "to ()>"
- + "from "
- + "()>:"
- + "to ()>"
- + "from ()>"
- + ":"
- + "from "
- + "()>:"
- + "to ()>"
- + "from "
- + ":"
- + "from "
- + ":"
- + "to "
- + "to ()>"
- + "to "
- + "to "
- + "to ()>"
- + "to "
- + "to ()>"
- + "to "
- + "to ()>"
- + "()>:"
- + "from "
- + "()>:"
- + "from ()>");
}
}
diff --git a/sootup.callgraph/src/test/java/sootup/callgraph/RapidTypeAnalysisAlgorithmTest.java b/sootup.callgraph/src/test/java/sootup/callgraph/RapidTypeAnalysisAlgorithmTest.java
index c5bcff4be5f..afb804127cb 100644
--- a/sootup.callgraph/src/test/java/sootup/callgraph/RapidTypeAnalysisAlgorithmTest.java
+++ b/sootup.callgraph/src/test/java/sootup/callgraph/RapidTypeAnalysisAlgorithmTest.java
@@ -142,27 +142,82 @@ public void testMiscExample1() {
"void",
Collections.emptyList());
- assertFalse(cg.containsCall(mainMethodSignature, constructorA));
- assertTrue(cg.containsCall(mainMethodSignature, constructorB));
- assertTrue(cg.containsCall(mainMethodSignature, constructorC));
- assertFalse(cg.containsCall(mainMethodSignature, constructorD));
- assertTrue(cg.containsCall(mainMethodSignature, constructorE));
+ assertFalse(
+ cg.containsCall(
+ mainMethodSignature,
+ constructorA,
+ getInvokableStmt(mainMethodSignature, constructorB)));
+ assertTrue(
+ cg.containsCall(
+ mainMethodSignature,
+ constructorB,
+ getInvokableStmt(mainMethodSignature, constructorB)));
+ assertTrue(
+ cg.containsCall(
+ mainMethodSignature,
+ constructorC,
+ getInvokableStmt(mainMethodSignature, constructorC)));
+ assertFalse(
+ cg.containsCall(
+ mainMethodSignature,
+ constructorD,
+ getInvokableStmt(mainMethodSignature, constructorC)));
+ assertTrue(
+ cg.containsCall(
+ mainMethodSignature,
+ constructorE,
+ getInvokableStmt(mainMethodSignature, constructorE)));
assertFalse(cg.containsMethod(staticMethodA));
- assertTrue(cg.containsCall(mainMethodSignature, staticMethodB));
+ assertTrue(
+ cg.containsCall(
+ mainMethodSignature,
+ staticMethodB,
+ getInvokableStmt(mainMethodSignature, staticMethodB)));
assertFalse(cg.containsMethod(staticMethodC));
assertFalse(cg.containsMethod(staticMethodD));
assertFalse(cg.containsMethod(staticMethodE));
assertFalse(cg.containsMethod(virtualMethodA));
- assertTrue(cg.containsCall(mainMethodSignature, virtualMethodB));
+ assertTrue(
+ cg.containsCall(
+ mainMethodSignature,
+ virtualMethodB,
+ getInvokableStmt(mainMethodSignature, virtualMethodA)));
assertFalse(cg.containsMethod(virtualMethodC));
- assertTrue(cg.containsCall(mainMethodSignature, virtualMethodD));
- assertTrue(cg.containsCall(mainMethodSignature, virtualMethodE));
-
- assertTrue(cg.containsCall(mainMethodSignature, clinitObject));
-
- assertEquals(8, cg.callsFrom(mainMethodSignature).size());
+ assertTrue(
+ cg.containsCall(
+ mainMethodSignature,
+ virtualMethodD,
+ getInvokableStmt(mainMethodSignature, virtualMethodA)));
+ assertTrue(
+ cg.containsCall(
+ mainMethodSignature,
+ virtualMethodE,
+ getInvokableStmt(mainMethodSignature, virtualMethodA)));
+
+ assertTrue(
+ cg.containsCall(
+ mainMethodSignature,
+ clinitObject,
+ getInvokableStmtNonInvokeExpr(mainMethodSignature, constructorB.getDeclClassType())));
+ assertTrue(
+ cg.containsCall(
+ mainMethodSignature,
+ clinitObject,
+ getInvokableStmtNonInvokeExpr(mainMethodSignature, constructorC.getDeclClassType())));
+ assertTrue(
+ cg.containsCall(
+ mainMethodSignature,
+ clinitObject,
+ getInvokableStmtNonInvokeExpr(mainMethodSignature, constructorE.getDeclClassType())));
+ assertTrue(
+ cg.containsCall(
+ mainMethodSignature,
+ clinitObject,
+ getInvokableStmt(mainMethodSignature, staticMethodB)));
+
+ assertEquals(11, cg.callsFrom(mainMethodSignature).size());
assertEquals(1, cg.callsTo(constructorB).size());
assertEquals(1, cg.callsTo(constructorC).size());
@@ -176,48 +231,6 @@ public void testMiscExample1() {
assertEquals(0, cg.callsFrom(virtualMethodB).size());
assertEquals(0, cg.callsFrom(virtualMethodD).size());
assertEquals(0, cg.callsFrom(virtualMethodE).size());
-
- assertEquals(
- cg.toString().replace("\n", "").replace("\t", ""),
- "GraphBasedCallGraph(13):"
- + "()>:"
- + "to ()>"
- + "from ()>"
- + "from ()>"
- + "from ()>"
- + "()>:"
- + "to ()>"
- + "from "
- + ":"
- + "from "
- + ":"
- + "from "
- + "()>:"
- + "to ()>"
- + "from "
- + "()>:"
- + "to ()>"
- + "from ()>"
- + ":"
- + "from "
- + "()>:"
- + "to ()>"
- + "from "
- + ":"
- + "from "
- + ":"
- + "to ()>"
- + "to "
- + "to "
- + "to ()>"
- + "to "
- + "to ()>"
- + "to "
- + "to ()>"
- + "()>:"
- + "from "
- + "()>:"
- + "from ()>");
}
@Test
@@ -251,9 +264,15 @@ public void testRevisitMethod() {
"int",
Collections.emptyList());
- assertFalse(cg.containsCall(alreadyVisitedMethod, newTargetA));
- assertTrue(cg.containsCall(alreadyVisitedMethod, newTargetB));
- assertTrue(cg.containsCall(alreadyVisitedMethod, newTargetC));
+ assertFalse(
+ cg.containsCall(
+ alreadyVisitedMethod, newTargetA, getInvokableStmt(alreadyVisitedMethod, newTargetA)));
+ assertTrue(
+ cg.containsCall(
+ alreadyVisitedMethod, newTargetB, getInvokableStmt(alreadyVisitedMethod, newTargetA)));
+ assertTrue(
+ cg.containsCall(
+ alreadyVisitedMethod, newTargetC, getInvokableStmt(alreadyVisitedMethod, newTargetA)));
}
@Test
@@ -287,9 +306,15 @@ public void testRecursiveRevisitMethod() {
"int",
Collections.emptyList());
- assertFalse(cg.containsCall(alreadyVisitedMethod, newTargetA));
- assertTrue(cg.containsCall(alreadyVisitedMethod, newTargetB));
- assertTrue(cg.containsCall(alreadyVisitedMethod, newTargetC));
+ assertFalse(
+ cg.containsCall(
+ alreadyVisitedMethod, newTargetA, getInvokableStmt(alreadyVisitedMethod, newTargetA)));
+ assertTrue(
+ cg.containsCall(
+ alreadyVisitedMethod, newTargetB, getInvokableStmt(alreadyVisitedMethod, newTargetA)));
+ assertTrue(
+ cg.containsCall(
+ alreadyVisitedMethod, newTargetC, getInvokableStmt(alreadyVisitedMethod, newTargetA)));
}
@Test
@@ -308,8 +333,16 @@ public void testInstantiatedClassInClinit() {
"method",
"void",
Collections.emptyList());
- assertTrue(cg.containsCall(mainMethodSignature, instantiatedClassMethod));
- assertFalse(cg.containsCall(mainMethodSignature, nonInstantiatedClassMethod));
+ assertTrue(
+ cg.containsCall(
+ mainMethodSignature,
+ instantiatedClassMethod,
+ getInvokableStmt(mainMethodSignature, nonInstantiatedClassMethod)));
+ assertFalse(
+ cg.containsCall(
+ mainMethodSignature,
+ nonInstantiatedClassMethod,
+ getInvokableStmt(mainMethodSignature, nonInstantiatedClassMethod)));
}
@Test
@@ -321,6 +354,10 @@ public void testLaterInstantiatedClass() {
"method",
"void",
Collections.emptyList());
- assertTrue(cg.containsCall(mainMethodSignature, instantiatedClassMethod));
+ assertTrue(
+ cg.containsCall(
+ mainMethodSignature,
+ instantiatedClassMethod,
+ getInvokableStmt(mainMethodSignature, instantiatedClassMethod)));
}
}
diff --git a/sootup.callgraph/src/test/resources/callgraph/Bugfixes/binary/Issue903.java b/sootup.callgraph/src/test/resources/callgraph/Bugfixes/binary/Issue903.java
new file mode 100644
index 00000000000..17787ab98c8
--- /dev/null
+++ b/sootup.callgraph/src/test/resources/callgraph/Bugfixes/binary/Issue903.java
@@ -0,0 +1,16 @@
+package issue903;
+import java.io.Closeable;
+
+interface A extends Closeable {
+ @Override
+ default void close(){
+ close();
+ }
+}
+
+class B implements A {
+ public static void main(String[] args) {
+ try (B b = new B()) {
+ }
+ }
+}
\ No newline at end of file
diff --git a/sootup.callgraph/src/test/resources/callgraph/Bugfixes/binary/issue903/A.class b/sootup.callgraph/src/test/resources/callgraph/Bugfixes/binary/issue903/A.class
new file mode 100644
index 00000000000..22eb6201655
Binary files /dev/null and b/sootup.callgraph/src/test/resources/callgraph/Bugfixes/binary/issue903/A.class differ
diff --git a/sootup.callgraph/src/test/resources/callgraph/Bugfixes/binary/issue903/B.class b/sootup.callgraph/src/test/resources/callgraph/Bugfixes/binary/issue903/B.class
new file mode 100644
index 00000000000..92100eb69ba
Binary files /dev/null and b/sootup.callgraph/src/test/resources/callgraph/Bugfixes/binary/issue903/B.class differ
diff --git a/sootup.callgraph/src/test/resources/callgraph/Misc/source/MultipleCallsToSameTarget.java b/sootup.callgraph/src/test/resources/callgraph/Misc/source/MultipleCallsToSameTarget.java
new file mode 100644
index 00000000000..dc4288c87d8
--- /dev/null
+++ b/sootup.callgraph/src/test/resources/callgraph/Misc/source/MultipleCallsToSameTarget.java
@@ -0,0 +1,44 @@
+package multi;
+
+class MultipleCallsToSameTarget {
+
+ public static void main(String[] args){
+ MultiCalls.method();
+ MultiCalls.method();
+
+ FieldLeft.field = 5;
+ FieldLeft.field = FieldLeft.method();
+
+ int f = FieldRight.field;
+ FieldLeft.field = FieldRight.field;
+
+ Instantiated in1 = new Instantiated();
+ Instantiated in2 = new Instantiated();
+
+ in1.method();
+ in2.method();
+ }
+}
+
+class MultiCalls{
+ static int field=3;
+
+ static void method(){
+
+ }
+}
+
+class FieldLeft{
+ static int field=3;
+ static void method(){}
+}
+
+class FieldRight{
+ static int field=3;
+}
+
+class Instantiated{
+ static int field=3;
+
+ void method(){}
+}
\ No newline at end of file
diff --git a/sootup.core/src/main/java/sootup/core/jimple/basic/JimpleComparator.java b/sootup.core/src/main/java/sootup/core/jimple/basic/JimpleComparator.java
index 5da7ae723de..d25e0944d74 100644
--- a/sootup.core/src/main/java/sootup/core/jimple/basic/JimpleComparator.java
+++ b/sootup.core/src/main/java/sootup/core/jimple/basic/JimpleComparator.java
@@ -143,7 +143,7 @@ public boolean caseBreakpointStmt(JBreakpointStmt stmt, Object o) {
public boolean caseInvokeStmt(JInvokeStmt stmt, Object o) {
return (o instanceof JInvokeStmt)
- && stmt.getInvokeExpr().equivTo(((JInvokeStmt) o).getInvokeExpr(), this);
+ && stmt.getInvokeExpr().get().equivTo(((JInvokeStmt) o).getInvokeExpr().get(), this);
}
public boolean caseAssignStmt(JAssignStmt stmt, Object o) {
diff --git a/sootup.core/src/main/java/sootup/core/jimple/common/expr/JNewArrayExpr.java b/sootup.core/src/main/java/sootup/core/jimple/common/expr/JNewArrayExpr.java
index 0cd4d94b0c8..44eef242070 100644
--- a/sootup.core/src/main/java/sootup/core/jimple/common/expr/JNewArrayExpr.java
+++ b/sootup.core/src/main/java/sootup/core/jimple/common/expr/JNewArrayExpr.java
@@ -31,6 +31,7 @@
import sootup.core.jimple.basic.Value;
import sootup.core.jimple.visitor.ExprVisitor;
import sootup.core.types.ArrayType;
+import sootup.core.types.PrimitiveType;
import sootup.core.types.Type;
import sootup.core.util.printer.StmtPrinter;
@@ -73,7 +74,7 @@ public int equivHashCode() {
@Override
public String toString() {
- return (Jimple.NEWARRAY + " (") + baseType.toString() + ")" + "[" + size.toString() + "]";
+ return (Jimple.NEWARRAY + " (") + baseType + ")" + "[" + size + "]";
}
/** Converts a parameter of type StmtPrinter to a string literal. */
@@ -131,4 +132,14 @@ public JNewArrayExpr withBaseType(@Nonnull Type baseType) {
public JNewArrayExpr withSize(@Nonnull Immediate size) {
return new JNewArrayExpr(baseType, size, identifierFactory);
}
+
+ public boolean isArrayOfPrimitives() {
+ if (baseType instanceof PrimitiveType) {
+ return true;
+ }
+ if (baseType instanceof ArrayType) {
+ return ((ArrayType) baseType).isArrayTypeOfPrimitives();
+ }
+ return false;
+ }
}
diff --git a/sootup.core/src/main/java/sootup/core/jimple/common/expr/JNewMultiArrayExpr.java b/sootup.core/src/main/java/sootup/core/jimple/common/expr/JNewMultiArrayExpr.java
index d6959fb82f7..3e834e28f75 100644
--- a/sootup.core/src/main/java/sootup/core/jimple/common/expr/JNewMultiArrayExpr.java
+++ b/sootup.core/src/main/java/sootup/core/jimple/common/expr/JNewMultiArrayExpr.java
@@ -120,7 +120,7 @@ public List getSizes() {
@Override
@Nonnull
public Stream getUses() {
- return Stream.concat(sizes.stream(), sizes.stream().flatMap(size -> size.getUses()));
+ return Stream.concat(sizes.stream(), sizes.stream().flatMap(Value::getUses));
}
@Nonnull
@@ -143,4 +143,8 @@ public JNewMultiArrayExpr withBaseType(@Nonnull ArrayType baseType) {
public JNewMultiArrayExpr withSizes(@Nonnull List sizes) {
return new JNewMultiArrayExpr(baseType, sizes);
}
+
+ public boolean isArrayOfPrimitives() {
+ return baseType.isArrayTypeOfPrimitives();
+ }
}
diff --git a/sootup.core/src/main/java/sootup/core/jimple/common/stmt/AbstractStmt.java b/sootup.core/src/main/java/sootup/core/jimple/common/stmt/AbstractStmt.java
index 6aa6d246c77..8251b0fee15 100644
--- a/sootup.core/src/main/java/sootup/core/jimple/common/stmt/AbstractStmt.java
+++ b/sootup.core/src/main/java/sootup/core/jimple/common/stmt/AbstractStmt.java
@@ -28,7 +28,6 @@
import sootup.core.jimple.basic.LValue;
import sootup.core.jimple.basic.StmtPositionInfo;
import sootup.core.jimple.basic.Value;
-import sootup.core.jimple.common.expr.AbstractInvokeExpr;
import sootup.core.jimple.common.ref.JArrayRef;
import sootup.core.jimple.common.ref.JFieldRef;
import sootup.core.jimple.visitor.ReplaceUseStmtVisitor;
@@ -66,10 +65,7 @@ public Optional getDef() {
@Nonnull
public Stream getUsesAndDefs() {
Optional def = getDef();
- if (def.isPresent()) {
- return Stream.concat(getUses(), Stream.of(def.get()));
- }
- return getUses();
+ return def.map(lValue -> Stream.concat(getUses(), Stream.of(lValue))).orElseGet(this::getUses);
}
/** Returns the amount of unexceptional successors the Stmt needs to have in the StmtGraph. */
@@ -78,20 +74,6 @@ public int getExpectedSuccessorCount() {
return 1;
}
- @Override
- public boolean containsInvokeExpr() {
- return false;
- }
-
- /**
- * This method must only be used for Stmts which contain an InvokeExpr (JInvokeStmt; possible in
- * JAssignStmt) check via containsInvokExpr().
- */
- @Override
- public AbstractInvokeExpr getInvokeExpr() {
- throw new RuntimeException("getInvokeExpr() called with no invokeExpr present!");
- }
-
@Override
public boolean containsArrayRef() {
return false;
@@ -144,4 +126,27 @@ public Stmt withNewUse(@Nonnull Value oldUse, @Nonnull Value newUse) {
}
return visitor.getResult();
}
+
+ /**
+ * Checks if the statement is an invokable statement, this means it either contains an invoke
+ * expression or causes a static initializer call
+ *
+ * @return true if the Object is an instance of {@link InvokableStmt}, otherwise false
+ */
+ @Override
+ public boolean isInvokableStmt() {
+ return this instanceof InvokableStmt;
+ }
+
+ /**
+ * Transforms the statement to an {@link InvokableStmt} if it is possible. if not it will throw an
+ * Exception. Before this method is used {@link #isInvokableStmt} should be called to prevent
+ * exceptions
+ *
+ * @return the typecast of this to InvokableStmt
+ */
+ @Override
+ public InvokableStmt asInvokableStmt() {
+ return (InvokableStmt) this;
+ }
}
diff --git a/sootup.core/src/main/java/sootup/core/jimple/common/stmt/InvokableStmt.java b/sootup.core/src/main/java/sootup/core/jimple/common/stmt/InvokableStmt.java
new file mode 100644
index 00000000000..11008b35fe3
--- /dev/null
+++ b/sootup.core/src/main/java/sootup/core/jimple/common/stmt/InvokableStmt.java
@@ -0,0 +1,57 @@
+package sootup.core.jimple.common.stmt;
+
+/*-
+ * #%L
+ * Soot - a J*va Optimization Framework
+ * %%
+ * Copyright (C) 2020 Markus Schmidt
+ * %%
+ * 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
+ * .
+ * #L%
+ */
+
+import java.util.Optional;
+import sootup.core.jimple.common.expr.AbstractInvokeExpr;
+
+/**
+ * Interface for Stmts that could invoke a different method which will be executed before the next
+ * statement is executed
+ */
+public interface InvokableStmt extends Stmt {
+
+ /**
+ * Checks if the invokable statement can potentially invoke a static initializer To cause a static
+ * initializer call, the statement contains a static method call, a new expression, or a static
+ * field reference.
+ *
+ * @return true if the statement can cause a static initializer call, and false if not.
+ */
+ boolean invokesStaticInitializer();
+
+ /**
+ * Checks if the invokable statement contains a invoke expression that defines the invoke.
+ *
+ * @return true if the statement contains an invoke expression, false if not
+ */
+ boolean containsInvokeExpr();
+
+ /**
+ * Returns the possible invoke expression in the invokable statement
+ *
+ * @return The optional contains the invoke expression of the invokable statement or is empty if
+ * there is no invoke expression.
+ */
+ Optional getInvokeExpr();
+}
diff --git a/sootup.core/src/main/java/sootup/core/jimple/common/stmt/JAssignStmt.java b/sootup.core/src/main/java/sootup/core/jimple/common/stmt/JAssignStmt.java
index 064eb5d359e..fb9f54c19ab 100644
--- a/sootup.core/src/main/java/sootup/core/jimple/common/stmt/JAssignStmt.java
+++ b/sootup.core/src/main/java/sootup/core/jimple/common/stmt/JAssignStmt.java
@@ -44,19 +44,26 @@
* #Value%
*/
+import java.util.Optional;
import javax.annotation.Nonnull;
import sootup.core.jimple.basic.*;
import sootup.core.jimple.common.expr.AbstractInvokeExpr;
import sootup.core.jimple.common.expr.Expr;
+import sootup.core.jimple.common.expr.JNewArrayExpr;
+import sootup.core.jimple.common.expr.JNewExpr;
+import sootup.core.jimple.common.expr.JNewMultiArrayExpr;
+import sootup.core.jimple.common.expr.JStaticInvokeExpr;
import sootup.core.jimple.common.ref.ConcreteRef;
import sootup.core.jimple.common.ref.JArrayRef;
import sootup.core.jimple.common.ref.JFieldRef;
import sootup.core.jimple.common.ref.JInstanceFieldRef;
+import sootup.core.jimple.common.ref.JStaticFieldRef;
import sootup.core.jimple.visitor.StmtVisitor;
import sootup.core.util.printer.StmtPrinter;
/** Represents the assignment of one value to another */
-public final class JAssignStmt extends AbstractDefinitionStmt implements FallsThroughStmt {
+public final class JAssignStmt extends AbstractDefinitionStmt
+ implements FallsThroughStmt, InvokableStmt {
@Nonnull final LValue leftOp;
@Nonnull final Value rightOp;
@@ -101,17 +108,38 @@ public boolean containsInvokeExpr() {
return getRightOp() instanceof AbstractInvokeExpr;
}
+ @Override
+ public boolean invokesStaticInitializer() {
+ if (getInvokeExpr().isPresent() && getInvokeExpr().get() instanceof JStaticInvokeExpr) {
+ return true;
+ }
+ Value rightOp = getRightOp();
+ if (rightOp instanceof JStaticFieldRef || getLeftOp() instanceof JStaticFieldRef) {
+ return true;
+ }
+ if (rightOp instanceof JNewExpr) {
+ return true;
+ }
+ if (rightOp instanceof JNewMultiArrayExpr) {
+ return !((JNewMultiArrayExpr) rightOp).isArrayOfPrimitives();
+ }
+ if (rightOp instanceof JNewArrayExpr) {
+ return !((JNewArrayExpr) rightOp).isArrayOfPrimitives();
+ }
+ return false;
+ }
+
/*
* (non-Javadoc)
*
* @see de.upb.sootup.jimple.common.stmt.AbstractStmt#getInvokeExpr()
*/
@Override
- public AbstractInvokeExpr getInvokeExpr() {
+ public Optional getInvokeExpr() {
if (!containsInvokeExpr()) {
- throw new RuntimeException("getInvokeExpr() called with no invokeExpr present!");
+ return Optional.empty();
}
- return (AbstractInvokeExpr) getRightOp();
+ return Optional.of((AbstractInvokeExpr) getRightOp());
}
/*
diff --git a/sootup.core/src/main/java/sootup/core/jimple/common/stmt/JInvokeStmt.java b/sootup.core/src/main/java/sootup/core/jimple/common/stmt/JInvokeStmt.java
index fa330bc47d6..2d5316701e3 100644
--- a/sootup.core/src/main/java/sootup/core/jimple/common/stmt/JInvokeStmt.java
+++ b/sootup.core/src/main/java/sootup/core/jimple/common/stmt/JInvokeStmt.java
@@ -21,17 +21,19 @@
* .
* #L%
*/
+import java.util.Optional;
import java.util.stream.Stream;
import javax.annotation.Nonnull;
import sootup.core.jimple.basic.JimpleComparator;
import sootup.core.jimple.basic.StmtPositionInfo;
import sootup.core.jimple.basic.Value;
import sootup.core.jimple.common.expr.AbstractInvokeExpr;
+import sootup.core.jimple.common.expr.JStaticInvokeExpr;
import sootup.core.jimple.visitor.StmtVisitor;
import sootup.core.util.printer.StmtPrinter;
/** A method call */
-public final class JInvokeStmt extends AbstractStmt implements FallsThroughStmt {
+public final class JInvokeStmt extends AbstractStmt implements FallsThroughStmt, InvokableStmt {
@Nonnull private final AbstractInvokeExpr invokeExpr;
@@ -46,6 +48,11 @@ public boolean containsInvokeExpr() {
return true;
}
+ @Override
+ public boolean invokesStaticInitializer() {
+ return invokeExpr instanceof JStaticInvokeExpr;
+ }
+
@Override
public String toString() {
return invokeExpr.toString();
@@ -58,8 +65,8 @@ public void toString(@Nonnull StmtPrinter up) {
@Override
@Nonnull
- public AbstractInvokeExpr getInvokeExpr() {
- return invokeExpr;
+ public Optional getInvokeExpr() {
+ return Optional.of(invokeExpr);
}
@Nonnull
@@ -100,6 +107,6 @@ public JInvokeStmt withInvokeExpr(AbstractInvokeExpr invokeExpr) {
@Nonnull
public JInvokeStmt withPositionInfo(@Nonnull StmtPositionInfo positionInfo) {
- return new JInvokeStmt(getInvokeExpr(), positionInfo);
+ return new JInvokeStmt(getInvokeExpr().get(), positionInfo);
}
}
diff --git a/sootup.core/src/main/java/sootup/core/jimple/common/stmt/Stmt.java b/sootup.core/src/main/java/sootup/core/jimple/common/stmt/Stmt.java
index 7d8e9d00406..c2d188fa150 100644
--- a/sootup.core/src/main/java/sootup/core/jimple/common/stmt/Stmt.java
+++ b/sootup.core/src/main/java/sootup/core/jimple/common/stmt/Stmt.java
@@ -27,7 +27,6 @@
import sootup.core.jimple.basic.LValue;
import sootup.core.jimple.basic.StmtPositionInfo;
import sootup.core.jimple.basic.Value;
-import sootup.core.jimple.common.expr.AbstractInvokeExpr;
import sootup.core.jimple.common.ref.JArrayRef;
import sootup.core.jimple.common.ref.JFieldRef;
import sootup.core.jimple.visitor.Acceptor;
@@ -60,10 +59,6 @@ public interface Stmt extends EquivTo, Acceptor {
void toString(@Nonnull StmtPrinter up);
- boolean containsInvokeExpr();
-
- AbstractInvokeExpr getInvokeExpr();
-
boolean containsArrayRef();
JArrayRef getArrayRef();
@@ -75,4 +70,8 @@ public interface Stmt extends EquivTo, Acceptor {
StmtPositionInfo getPositionInfo();
Stmt withNewUse(@Nonnull Value oldUse, @Nonnull Value newUse);
+
+ boolean isInvokableStmt();
+
+ InvokableStmt asInvokableStmt();
}
diff --git a/sootup.core/src/main/java/sootup/core/jimple/visitor/ReplaceUseStmtVisitor.java b/sootup.core/src/main/java/sootup/core/jimple/visitor/ReplaceUseStmtVisitor.java
index 7719bd2ca52..c68e5921aef 100644
--- a/sootup.core/src/main/java/sootup/core/jimple/visitor/ReplaceUseStmtVisitor.java
+++ b/sootup.core/src/main/java/sootup/core/jimple/visitor/ReplaceUseStmtVisitor.java
@@ -58,7 +58,7 @@ public void caseBreakpointStmt(@Nonnull JBreakpointStmt stmt) {
@Override
public void caseInvokeStmt(@Nonnull JInvokeStmt stmt) {
- Expr invokeExpr = stmt.getInvokeExpr();
+ Expr invokeExpr = stmt.getInvokeExpr().get();
exprVisitor.init(oldUse, newUse);
invokeExpr.accept(exprVisitor);
diff --git a/sootup.core/src/main/java/sootup/core/types/ArrayType.java b/sootup.core/src/main/java/sootup/core/types/ArrayType.java
index 0fefc0fc8b0..df57ef7ed62 100644
--- a/sootup.core/src/main/java/sootup/core/types/ArrayType.java
+++ b/sootup.core/src/main/java/sootup/core/types/ArrayType.java
@@ -99,4 +99,18 @@ public Type getElementType() {
public void accept(@Nonnull TypeVisitor v) {
v.caseArrayType();
}
+
+ public boolean isArrayTypeOfPrimitives() {
+ return isArrayTypeOfPrimitives(baseType);
+ }
+
+ public static boolean isArrayTypeOfPrimitives(Type type) {
+ if (type instanceof PrimitiveType) {
+ return true;
+ }
+ if (type instanceof ArrayType) {
+ return isArrayTypeOfPrimitives(((ArrayType) type).getBaseType());
+ }
+ return false;
+ }
}
diff --git a/sootup.core/src/main/java/sootup/core/util/Utils.java b/sootup.core/src/main/java/sootup/core/util/Utils.java
index d9d51cb70e1..3a7c75ce1c8 100644
--- a/sootup.core/src/main/java/sootup/core/util/Utils.java
+++ b/sootup.core/src/main/java/sootup/core/util/Utils.java
@@ -217,10 +217,6 @@ public static ArrayList filterJimple(Stream stream) {
.collect(Collectors.toCollection(ArrayList::new));
}
- public static void generateJimpleForTest(@Nonnull SootMethod m) {
- System.out.println(generateJimpleForTest(m.getBody()));
- }
-
/** Helper for writing tests . */
public static String generateJimpleForTest(@Nonnull Body b) {
ArrayList arr = filterJimple(Utils.bodyStmtsAsStrings(b).stream());
diff --git a/sootup.core/src/main/java/sootup/core/validation/NewValidator.java b/sootup.core/src/main/java/sootup/core/validation/NewValidator.java
index 693c693524e..214dd1c1b49 100644
--- a/sootup.core/src/main/java/sootup/core/validation/NewValidator.java
+++ b/sootup.core/src/main/java/sootup/core/validation/NewValidator.java
@@ -96,12 +96,12 @@ private boolean checkForInitializerOnPath(
continue;
}
if (!newStmt.equals(curStmt)) {
- if (curStmt.containsInvokeExpr()) {
- AbstractInvokeExpr expr = curStmt.getInvokeExpr();
+ if (curStmt.isInvokableStmt() && curStmt.asInvokableStmt().containsInvokeExpr()) {
+ AbstractInvokeExpr expr = curStmt.asInvokableStmt().getInvokeExpr().get();
if (!(expr instanceof JSpecialInvokeExpr)) {
exception.add(
new ValidationException(
- curStmt.getInvokeExpr(),
+ expr,
" Method calls may only be used with specialinvoke.")); // At least we
// found an initializer, so we return true...
return true;
@@ -109,7 +109,7 @@ private boolean checkForInitializerOnPath(
if (!(curStmt instanceof JInvokeStmt)) {
exception.add(
new ValidationException(
- curStmt.getInvokeExpr(),
+ expr,
" methods may only be called with invoke statements.")); // At least we
// found an initializer, so we return true...
return true;
diff --git a/sootup.core/src/test/java/sootup/core/TestUtil.java b/sootup.core/src/test/java/sootup/core/TestUtil.java
new file mode 100644
index 00000000000..6ebd19ae2d6
--- /dev/null
+++ b/sootup.core/src/test/java/sootup/core/TestUtil.java
@@ -0,0 +1,230 @@
+package sootup.core;
+
+import java.util.Collections;
+import sootup.core.jimple.basic.LValue;
+import sootup.core.jimple.basic.Local;
+import sootup.core.jimple.basic.SimpleStmtPositionInfo;
+import sootup.core.jimple.basic.Value;
+import sootup.core.jimple.common.expr.AbstractInvokeExpr;
+import sootup.core.jimple.common.expr.JInterfaceInvokeExpr;
+import sootup.core.jimple.common.expr.JSpecialInvokeExpr;
+import sootup.core.jimple.common.expr.JStaticInvokeExpr;
+import sootup.core.jimple.common.expr.JVirtualInvokeExpr;
+import sootup.core.jimple.common.ref.JFieldRef;
+import sootup.core.jimple.common.ref.JInstanceFieldRef;
+import sootup.core.jimple.common.ref.JStaticFieldRef;
+import sootup.core.jimple.common.stmt.JAssignStmt;
+import sootup.core.signatures.FieldSignature;
+import sootup.core.signatures.FieldSubSignature;
+import sootup.core.signatures.MethodSignature;
+import sootup.core.signatures.MethodSubSignature;
+import sootup.core.signatures.PackageName;
+import sootup.core.types.ClassType;
+import sootup.core.types.PrimitiveType.IntType;
+
+public class TestUtil {
+
+ /**
+ * creates a dummy SimpleStmtPositionInfo lineNumber 1
+ *
+ * @return a dummy SimpleStmtPositionInfo
+ */
+ public static SimpleStmtPositionInfo createDummySimpleStmtPositionInfo() {
+ return new SimpleStmtPositionInfo(1);
+ }
+
+ /**
+ * creates a dummy method signature Class Type: dummy class type SubSignature: dummy method sub
+ * signature
+ *
+ * @return a dummy method signature
+ */
+ public static MethodSignature createDummyMethodSignature() {
+ return new MethodSignature(
+ TestUtil.createDummyClassType(), TestUtil.createDummyMethodSubSignature());
+ }
+
+ /**
+ * creates a dummy method sub signature name: test return type: int parameter list: empty
+ *
+ * @return a dummy method sub signature
+ */
+ public static MethodSubSignature createDummyMethodSubSignature() {
+ return new MethodSubSignature("test", Collections.emptyList(), IntType.getInstance());
+ }
+
+ /**
+ * creates a dummy field signature Class Type: dummy class type SubSignature: dummy field sub
+ * signature
+ *
+ * @return a dummy field signature
+ */
+ public static FieldSignature createDummyFieldSignature() {
+ return new FieldSignature(
+ TestUtil.createDummyClassType(), TestUtil.createDummyFieldSubSignature());
+ }
+
+ /**
+ * creates a dummy field sub signature name: test type: int
+ *
+ * @return a dummy field sub signature
+ */
+ public static FieldSubSignature createDummyFieldSubSignature() {
+ return new FieldSubSignature("test", IntType.getInstance());
+ }
+
+ /**
+ * creates a dummy Local for an Object Name a Type dummy class type
+ *
+ * @return a dummy Local for an Object
+ */
+ public static Local createDummyLocalForObject() {
+ return new Local("a", TestUtil.createDummyClassType());
+ }
+
+ /**
+ * creates a dummy Local for an Int Name b Type int
+ *
+ * @return a dummy Local for a int value
+ */
+ public static Local createDummyLocalForInt() {
+ return new Local("b", IntType.getInstance());
+ }
+
+ /**
+ * will return a dummy virtual invoke expression local is called a and has the class type Test
+ * method signature is <Test: int test ()> arguments is an empty list
+ */
+ public static JVirtualInvokeExpr createDummyVirtualInvokeExpr() {
+ Local local = TestUtil.createDummyLocalForObject();
+ MethodSignature methodSignature = TestUtil.createDummyMethodSignature();
+ return new JVirtualInvokeExpr(local, methodSignature, Collections.emptyList());
+ }
+
+ /**
+ * will return a dummy special invoke expression local is called a and has the class type Test
+ * method signature is <Test: int test ()> arguments is an empty list
+ */
+ public static JSpecialInvokeExpr createDummySpecialInvokeExpr() {
+ Local local = TestUtil.createDummyLocalForObject();
+ MethodSignature methodSignature = TestUtil.createDummyMethodSignature();
+ return new JSpecialInvokeExpr(local, methodSignature, Collections.emptyList());
+ }
+
+ /**
+ * will return a dummy interface invoke expression local is called a and has the class type Test
+ * method signature is <Test: int test ()> arguments is an empty list
+ */
+ public static JInterfaceInvokeExpr createDummyInterfaceInvokeExpr() {
+ Local local = TestUtil.createDummyLocalForObject();
+ MethodSignature methodSignature = TestUtil.createDummyMethodSignature();
+ return new JInterfaceInvokeExpr(local, methodSignature, Collections.emptyList());
+ }
+
+ /**
+ * will return a dummy static invoke expression method signature is <Test: int test ()>
+ * arguments is an empty list
+ */
+ public static JStaticInvokeExpr createDummyStaticInvokeExpr() {
+ MethodSignature methodSignature = TestUtil.createDummyMethodSignature();
+ return new JStaticInvokeExpr(methodSignature, Collections.emptyList());
+ }
+
+ /**
+ * creates a dummy static field reference Signature: dummy Field Signature
+ *
+ * @return a dummy JStaticFieldRef
+ */
+ public static JStaticFieldRef createDummyStaticFieldRef() {
+ return new JStaticFieldRef(TestUtil.createDummyFieldSignature());
+ }
+
+ /**
+ * creates a dummy instance field reference local: dummy local Signature: dummy Field Signature
+ *
+ * @return a dummy JInstanceFieldRef
+ */
+ public static JInstanceFieldRef createDummyInstanceFieldRef() {
+ return new JInstanceFieldRef(
+ TestUtil.createDummyLocalForInt(), TestUtil.createDummyFieldSignature());
+ }
+
+ /**
+ * creates a dummy Class type Classname Test Package name test Fully Qualified Name test.Test
+ *
+ * @return a dummy class type
+ */
+ public static ClassType createDummyClassType() {
+ return new ClassType() {
+ @Override
+ public boolean isBuiltInClass() {
+ return false;
+ }
+
+ @Override
+ public String getFullyQualifiedName() {
+ return "Test";
+ }
+
+ @Override
+ public String getClassName() {
+ return "Test";
+ }
+
+ @Override
+ public PackageName getPackageName() {
+ return new PackageName("test");
+ }
+ };
+ }
+
+ /**
+ * will return a dummy assignment statement with an invoke expression the left value will be the
+ * dummy int local the right value will be the given invoke expression stmt position is the dummy
+ * SimpleStatementPositionInfo
+ *
+ * @param invokeExpr the invokeExpr in the dummy assignment statement
+ * @return a dummy JAssignStmt with a static invoke expression
+ */
+ public static JAssignStmt createDummyAssignStmtWithExpr(AbstractInvokeExpr invokeExpr) {
+ Local local = TestUtil.createDummyLocalForInt();
+ SimpleStmtPositionInfo pos = TestUtil.createDummySimpleStmtPositionInfo();
+ return new JAssignStmt(local, invokeExpr, pos);
+ }
+
+ /**
+ * will return a dummy assignment statement the left value will be the dummy static field ref the
+ * right value will be the dummy local stmt position is the dummy SimpleStatementPositionInfo
+ *
+ * @return a dummy JAssignStmt with a static field ref on the left side
+ */
+ public static JAssignStmt createDummyAssignStmtWithStaticFieldRefLeft() {
+ JFieldRef fieldRef = TestUtil.createDummyStaticFieldRef();
+ SimpleStmtPositionInfo pos = TestUtil.createDummySimpleStmtPositionInfo();
+ return new JAssignStmt(fieldRef, TestUtil.createDummyLocalForInt(), pos);
+ }
+
+ /**
+ * will return a dummy assignment statement stmt position is the dummy SimpleStatementPositionInfo
+ *
+ * @param left defines the left value of the dummy assign statement
+ * @param right defines the right value of the dummy assign statement
+ * @return a dummy JAssignStmt with an instance field ref on the left side
+ */
+ public static JAssignStmt createDummyAssignStmt(LValue left, Value right) {
+ SimpleStmtPositionInfo pos = TestUtil.createDummySimpleStmtPositionInfo();
+ return new JAssignStmt(left, right, pos);
+ }
+
+ /**
+ * will return a dummy assignment statement the right value will be the dummy local the left value
+ * will be the dummy local stmt position is the dummy SimpleStatementPositionInfo
+ *
+ * @return a dummy JAssignStmt with a static field ref on the left side
+ */
+ public static JAssignStmt createDummyAssignStmtWithLocals() {
+ SimpleStmtPositionInfo pos = TestUtil.createDummySimpleStmtPositionInfo();
+ return new JAssignStmt(
+ TestUtil.createDummyLocalForInt(), TestUtil.createDummyLocalForInt(), pos);
+ }
+}
diff --git a/sootup.core/src/test/java/sootup/core/jimple/common/stmt/JAssignStmtTest.java b/sootup.core/src/test/java/sootup/core/jimple/common/stmt/JAssignStmtTest.java
new file mode 100644
index 00000000000..f582954ea59
--- /dev/null
+++ b/sootup.core/src/test/java/sootup/core/jimple/common/stmt/JAssignStmtTest.java
@@ -0,0 +1,115 @@
+package sootup.core.jimple.common.stmt;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Tag;
+import org.junit.jupiter.api.Test;
+import sootup.core.TestUtil;
+import sootup.core.jimple.basic.Local;
+import sootup.core.jimple.common.ref.JInstanceFieldRef;
+import sootup.core.jimple.common.ref.JStaticFieldRef;
+
+@Tag("Java8")
+public class JAssignStmtTest {
+
+ @Test
+ public void testInvokesStaticInitializer() {
+ JStaticFieldRef staticFieldRef = TestUtil.createDummyStaticFieldRef();
+ JInstanceFieldRef instanceFieldRef = TestUtil.createDummyInstanceFieldRef();
+ Local local = TestUtil.createDummyLocalForInt();
+
+ assertTrue(
+ TestUtil.createDummyAssignStmtWithExpr(TestUtil.createDummyStaticInvokeExpr())
+ .invokesStaticInitializer());
+ assertFalse(
+ TestUtil.createDummyAssignStmtWithExpr(TestUtil.createDummyVirtualInvokeExpr())
+ .invokesStaticInitializer());
+ assertFalse(
+ TestUtil.createDummyAssignStmtWithExpr(TestUtil.createDummySpecialInvokeExpr())
+ .invokesStaticInitializer());
+ assertFalse(
+ TestUtil.createDummyAssignStmtWithExpr(TestUtil.createDummyInterfaceInvokeExpr())
+ .invokesStaticInitializer());
+
+ assertTrue(TestUtil.createDummyAssignStmt(staticFieldRef, local).invokesStaticInitializer());
+ assertTrue(TestUtil.createDummyAssignStmt(local, staticFieldRef).invokesStaticInitializer());
+ assertTrue(
+ TestUtil.createDummyAssignStmt(staticFieldRef, staticFieldRef).invokesStaticInitializer());
+ assertTrue(
+ TestUtil.createDummyAssignStmt(instanceFieldRef, staticFieldRef)
+ .invokesStaticInitializer());
+ assertTrue(
+ TestUtil.createDummyAssignStmt(staticFieldRef, instanceFieldRef)
+ .invokesStaticInitializer());
+
+ assertFalse(TestUtil.createDummyAssignStmt(instanceFieldRef, local).invokesStaticInitializer());
+ assertFalse(TestUtil.createDummyAssignStmt(local, instanceFieldRef).invokesStaticInitializer());
+ assertFalse(
+ TestUtil.createDummyAssignStmt(instanceFieldRef, instanceFieldRef)
+ .invokesStaticInitializer());
+ assertFalse(TestUtil.createDummyAssignStmtWithLocals().invokesStaticInitializer());
+ }
+
+ @Test
+ public void testContainsInvokeExpr() {
+ JStaticFieldRef staticFieldRef = TestUtil.createDummyStaticFieldRef();
+ JInstanceFieldRef instanceFieldRef = TestUtil.createDummyInstanceFieldRef();
+ Local local = TestUtil.createDummyLocalForInt();
+
+ assertTrue(
+ TestUtil.createDummyAssignStmtWithExpr(TestUtil.createDummyStaticInvokeExpr())
+ .containsInvokeExpr());
+
+ assertFalse(TestUtil.createDummyAssignStmt(staticFieldRef, local).containsInvokeExpr());
+ assertFalse(TestUtil.createDummyAssignStmt(local, staticFieldRef).containsInvokeExpr());
+ assertFalse(
+ TestUtil.createDummyAssignStmt(staticFieldRef, staticFieldRef).containsInvokeExpr());
+ assertFalse(
+ TestUtil.createDummyAssignStmt(instanceFieldRef, staticFieldRef).containsInvokeExpr());
+ assertFalse(
+ TestUtil.createDummyAssignStmt(staticFieldRef, instanceFieldRef).containsInvokeExpr());
+ assertFalse(TestUtil.createDummyAssignStmt(instanceFieldRef, local).containsInvokeExpr());
+ assertFalse(TestUtil.createDummyAssignStmt(local, instanceFieldRef).containsInvokeExpr());
+ assertFalse(
+ TestUtil.createDummyAssignStmt(instanceFieldRef, instanceFieldRef).containsInvokeExpr());
+ assertFalse(TestUtil.createDummyAssignStmtWithLocals().containsInvokeExpr());
+ }
+
+ @Test
+ public void testGetInvokeExpr() {
+ JStaticFieldRef staticFieldRef = TestUtil.createDummyStaticFieldRef();
+ JInstanceFieldRef instanceFieldRef = TestUtil.createDummyInstanceFieldRef();
+ Local local = TestUtil.createDummyLocalForInt();
+
+ assertEquals(
+ TestUtil.createDummyStaticInvokeExpr().toString(),
+ TestUtil.createDummyAssignStmtWithExpr(TestUtil.createDummyStaticInvokeExpr())
+ .getInvokeExpr()
+ .get()
+ .toString());
+
+ assertFalse(TestUtil.createDummyAssignStmt(staticFieldRef, local).getInvokeExpr().isPresent());
+ assertFalse(TestUtil.createDummyAssignStmt(local, staticFieldRef).getInvokeExpr().isPresent());
+ assertFalse(
+ TestUtil.createDummyAssignStmt(staticFieldRef, staticFieldRef).getInvokeExpr().isPresent());
+ assertFalse(
+ TestUtil.createDummyAssignStmt(instanceFieldRef, staticFieldRef)
+ .getInvokeExpr()
+ .isPresent());
+ assertFalse(
+ TestUtil.createDummyAssignStmt(staticFieldRef, instanceFieldRef)
+ .getInvokeExpr()
+ .isPresent());
+ assertFalse(
+ TestUtil.createDummyAssignStmt(instanceFieldRef, local).getInvokeExpr().isPresent());
+ assertFalse(
+ TestUtil.createDummyAssignStmt(local, instanceFieldRef).getInvokeExpr().isPresent());
+ assertFalse(
+ TestUtil.createDummyAssignStmt(instanceFieldRef, instanceFieldRef)
+ .getInvokeExpr()
+ .isPresent());
+ assertFalse(TestUtil.createDummyAssignStmtWithLocals().getInvokeExpr().isPresent());
+ }
+}
diff --git a/sootup.core/src/test/java/sootup/core/jimple/common/stmt/JInvokeStmtTest.java b/sootup.core/src/test/java/sootup/core/jimple/common/stmt/JInvokeStmtTest.java
new file mode 100644
index 00000000000..4e2f6914ddb
--- /dev/null
+++ b/sootup.core/src/test/java/sootup/core/jimple/common/stmt/JInvokeStmtTest.java
@@ -0,0 +1,73 @@
+package sootup.core.jimple.common.stmt;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Tag;
+import org.junit.jupiter.api.Test;
+import sootup.core.TestUtil;
+import sootup.core.jimple.basic.SimpleStmtPositionInfo;
+import sootup.core.jimple.common.expr.JInterfaceInvokeExpr;
+import sootup.core.jimple.common.expr.JSpecialInvokeExpr;
+import sootup.core.jimple.common.expr.JStaticInvokeExpr;
+import sootup.core.jimple.common.expr.JVirtualInvokeExpr;
+
+@Tag("Java8")
+public class JInvokeStmtTest {
+
+ @Test
+ public void testContainsInvokeExpr() {
+ SimpleStmtPositionInfo pos = new SimpleStmtPositionInfo(1);
+
+ JInvokeStmt invokeStmt = new JInvokeStmt(TestUtil.createDummyStaticInvokeExpr(), pos);
+ assertTrue(invokeStmt.containsInvokeExpr());
+
+ JInvokeStmt invokeStmt1 = new JInvokeStmt(TestUtil.createDummyInterfaceInvokeExpr(), pos);
+ assertTrue(invokeStmt1.containsInvokeExpr());
+
+ JInvokeStmt invokeStmt2 = new JInvokeStmt(TestUtil.createDummySpecialInvokeExpr(), pos);
+ assertTrue(invokeStmt2.containsInvokeExpr());
+
+ JInvokeStmt invokeStmt3 = new JInvokeStmt(TestUtil.createDummyVirtualInvokeExpr(), pos);
+ assertTrue(invokeStmt3.containsInvokeExpr());
+ }
+
+ @Test
+ public void testInvokesStaticInitializer() {
+ SimpleStmtPositionInfo pos = new SimpleStmtPositionInfo(1);
+
+ JInvokeStmt invokeStmt = new JInvokeStmt(TestUtil.createDummyStaticInvokeExpr(), pos);
+ assertTrue(invokeStmt.invokesStaticInitializer());
+
+ JInvokeStmt invokeStmt1 = new JInvokeStmt(TestUtil.createDummyInterfaceInvokeExpr(), pos);
+ assertFalse(invokeStmt1.invokesStaticInitializer());
+
+ JInvokeStmt invokeStmt2 = new JInvokeStmt(TestUtil.createDummySpecialInvokeExpr(), pos);
+ assertFalse(invokeStmt2.invokesStaticInitializer());
+
+ JInvokeStmt invokeStmt3 = new JInvokeStmt(TestUtil.createDummyVirtualInvokeExpr(), pos);
+ assertFalse(invokeStmt3.invokesStaticInitializer());
+ }
+
+ @Test
+ public void testGetInvokeExpr() {
+ SimpleStmtPositionInfo pos = new SimpleStmtPositionInfo(1);
+
+ JStaticInvokeExpr staticExpr = TestUtil.createDummyStaticInvokeExpr();
+ JInvokeStmt invokeStmt = new JInvokeStmt(staticExpr, pos);
+ assertEquals(staticExpr, invokeStmt.getInvokeExpr().get());
+
+ JVirtualInvokeExpr virtualExpr = TestUtil.createDummyVirtualInvokeExpr();
+ JInvokeStmt invokeStmt1 = new JInvokeStmt(virtualExpr, pos);
+ assertEquals(virtualExpr, invokeStmt1.getInvokeExpr().get());
+
+ JSpecialInvokeExpr specialExpr = TestUtil.createDummySpecialInvokeExpr();
+ JInvokeStmt invokeStmt2 = new JInvokeStmt(specialExpr, pos);
+ assertEquals(specialExpr, invokeStmt2.getInvokeExpr().get());
+
+ JInterfaceInvokeExpr interfaceExpr = TestUtil.createDummyInterfaceInvokeExpr();
+ JInvokeStmt invokeStmt3 = new JInvokeStmt(interfaceExpr, pos);
+ assertEquals(interfaceExpr.toString(), invokeStmt3.getInvokeExpr().get().toString());
+ }
+}
diff --git a/sootup.examples/src/test/java/sootup/examples/basicSetup/BasicSetup.java b/sootup.examples/src/test/java/sootup/examples/basicSetup/BasicSetup.java
index aa1dfb0d215..a72e8bd9c2a 100644
--- a/sootup.examples/src/test/java/sootup/examples/basicSetup/BasicSetup.java
+++ b/sootup.examples/src/test/java/sootup/examples/basicSetup/BasicSetup.java
@@ -12,6 +12,7 @@
import sootup.core.jimple.common.stmt.JInvokeStmt;
import sootup.core.model.SootClass;
import sootup.core.model.SootMethod;
+import sootup.core.model.SourceType;
import sootup.core.signatures.MethodSignature;
import sootup.core.types.ClassType;
import sootup.core.views.View;
@@ -28,7 +29,8 @@ public void createByteCodeProject() {
// Create a AnalysisInputLocation, which points to a directory. All class files will be loaded
// from the directory
Path pathToBinary = Paths.get("src/test/resources/BasicSetup/binary");
- AnalysisInputLocation inputLocation = PathBasedAnalysisInputLocation.create(pathToBinary, null);
+ AnalysisInputLocation inputLocation =
+ PathBasedAnalysisInputLocation.create(pathToBinary, SourceType.Application);
// Create a view for project, which allows us to retrieve classes
View view = new JavaView(inputLocation);
@@ -64,8 +66,10 @@ public void createByteCodeProject() {
.anyMatch(
stmt ->
stmt instanceof JInvokeStmt
- && stmt.getInvokeExpr() instanceof JVirtualInvokeExpr
- && stmt.getInvokeExpr()
+ && ((JInvokeStmt) stmt).getInvokeExpr().get() instanceof JVirtualInvokeExpr
+ && ((JInvokeStmt) stmt)
+ .getInvokeExpr()
+ .get()
.getArg(0)
.equivTo(JavaJimple.getInstance().newStringConstant("Hello World!"))));
}
diff --git a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/RuntimeJarConversionTests.java b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/RuntimeJarConversionTests.java
index ff256e051d8..18110fc5ef3 100644
--- a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/RuntimeJarConversionTests.java
+++ b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/RuntimeJarConversionTests.java
@@ -24,7 +24,7 @@
@Tag("Java8")
public class RuntimeJarConversionTests {
- private static boolean debug = true;
+ private static boolean debug = false;
@Test
public void testJarWithDefaultInterceptors() {
@@ -61,7 +61,6 @@ private static void convertInputLocation(AnalysisInputLocation inputLocation) {
.peek(
javaSootMethod -> {
try {
- System.out.println(javaSootMethod.getSignature());
javaSootMethod.getBody();
} catch (Exception e) {
e.printStackTrace();
diff --git a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java14/RecordTest.java b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java14/RecordTest.java
index 675193277c1..93b17696930 100644
--- a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java14/RecordTest.java
+++ b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/minimaltestsuite/java14/RecordTest.java
@@ -6,13 +6,14 @@
import categories.TestCategories;
import java.util.Collections;
import java.util.List;
+import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import sootup.core.jimple.basic.Immediate;
import sootup.core.jimple.common.expr.JDynamicInvokeExpr;
-import sootup.core.jimple.common.stmt.Stmt;
+import sootup.core.jimple.common.stmt.InvokableStmt;
import sootup.core.model.SootMethod;
import sootup.core.signatures.FieldSignature;
import sootup.core.signatures.MethodSignature;
@@ -58,8 +59,12 @@ public void test() {
assertJimpleStmts(method, expectedBodyStmts());
List