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 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 dynamicInvokes = method.getBody().getStmts().stream() - .filter(Stmt::containsInvokeExpr) - .map(Stmt::getInvokeExpr) + .filter(stmt -> stmt instanceof InvokableStmt) + .map(stmt -> (InvokableStmt) stmt) + .filter(InvokableStmt::containsInvokeExpr) + .map(InvokableStmt::getInvokeExpr) + .filter(Optional::isPresent) + .map(Optional::get) .filter(abstractInvokeExpr -> abstractInvokeExpr instanceof JDynamicInvokeExpr) .map(abstractInvokeExpr -> (JDynamicInvokeExpr) abstractInvokeExpr) .collect(Collectors.toList()); diff --git a/sootup.java.core/src/main/java/sootup/java/core/interceptors/CopyPropagator.java b/sootup.java.core/src/main/java/sootup/java/core/interceptors/CopyPropagator.java index d9d5808ed22..6f397ef157c 100644 --- a/sootup.java.core/src/main/java/sootup/java/core/interceptors/CopyPropagator.java +++ b/sootup.java.core/src/main/java/sootup/java/core/interceptors/CopyPropagator.java @@ -34,6 +34,7 @@ import sootup.core.jimple.common.constant.NullConstant; import sootup.core.jimple.common.expr.JCastExpr; import sootup.core.jimple.common.stmt.AbstractDefinitionStmt; +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.Body; @@ -74,7 +75,8 @@ public void interceptBody(@Nonnull Body.BodyBuilder builder, @Nonnull View view) AbstractDefinitionStmt defStmt = (AbstractDefinitionStmt) defsOfUse.get(0); Value rhs = defStmt.getRightOp(); // if rhs is a constant, then replace use, if it is possible - if (rhs instanceof Constant && !stmt.containsInvokeExpr()) { + if (rhs instanceof Constant + && !((stmt instanceof InvokableStmt) && ((InvokableStmt) stmt).containsInvokeExpr())) { replaceUse(stmtGraph, stmt, use, rhs); } diff --git a/sootup.java.core/src/main/java/sootup/java/core/interceptors/DeadAssignmentEliminator.java b/sootup.java.core/src/main/java/sootup/java/core/interceptors/DeadAssignmentEliminator.java index dfebe2cda3b..50d5159ec35 100644 --- a/sootup.java.core/src/main/java/sootup/java/core/interceptors/DeadAssignmentEliminator.java +++ b/sootup.java.core/src/main/java/sootup/java/core/interceptors/DeadAssignmentEliminator.java @@ -240,7 +240,7 @@ public void interceptBody(@Nonnull Body.BodyBuilder builder, @Nonnull View view) for (JAssignStmt assignStmt : postProcess) { // Transform it into a simple invoke Stmt newInvoke = - Jimple.newInvokeStmt(assignStmt.getInvokeExpr(), assignStmt.getPositionInfo()); + Jimple.newInvokeStmt(assignStmt.getInvokeExpr().get(), assignStmt.getPositionInfo()); stmtGraph.replaceNode(assignStmt, newInvoke); builder.removeDefLocalsOf(assignStmt); } diff --git a/sootup.java.core/src/main/java/sootup/java/core/interceptors/typeresolving/TypeChecker.java b/sootup.java.core/src/main/java/sootup/java/core/interceptors/typeresolving/TypeChecker.java index e6186ede693..bde15e23c16 100644 --- a/sootup.java.core/src/main/java/sootup/java/core/interceptors/typeresolving/TypeChecker.java +++ b/sootup.java.core/src/main/java/sootup/java/core/interceptors/typeresolving/TypeChecker.java @@ -74,7 +74,7 @@ public TypeChecker( @Override public void caseInvokeStmt(@Nonnull JInvokeStmt stmt) { - handleInvokeExpr(stmt.getInvokeExpr(), stmt); + handleInvokeExpr(stmt.getInvokeExpr().get(), stmt); } @Override diff --git a/sootup.qilin/src/main/java/qilin/core/builder/CallGraphBuilder.java b/sootup.qilin/src/main/java/qilin/core/builder/CallGraphBuilder.java index b914b53e1af..e388732bb69 100644 --- a/sootup.qilin/src/main/java/qilin/core/builder/CallGraphBuilder.java +++ b/sootup.qilin/src/main/java/qilin/core/builder/CallGraphBuilder.java @@ -41,6 +41,7 @@ import sootup.core.jimple.common.expr.AbstractInvokeExpr; import sootup.core.jimple.common.expr.JSpecialInvokeExpr; import sootup.core.jimple.common.expr.JStaticInvokeExpr; +import sootup.core.jimple.common.stmt.InvokableStmt; import sootup.core.jimple.common.stmt.JAssignStmt; import sootup.core.jimple.common.stmt.JInvokeStmt; import sootup.core.jimple.common.stmt.Stmt; @@ -54,7 +55,7 @@ public class CallGraphBuilder { private static final ClassType clRunnable = PTAUtils.getClassType("java.lang.Runnable"); protected final Map> receiverToSites; - protected final Map> methodToInvokeStmt; + protected final Map> methodToInvokeStmt; protected final Set reachMethods; private ChunkedQueue rmQueue; @@ -177,7 +178,11 @@ protected void dispatch(AllocNode receiverNode, VirtualCallSite site) { } private void addVirtualEdge( - ContextMethod caller, Stmt callStmt, SootMethod callee, Kind kind, AllocNode receiverNode) { + ContextMethod caller, + InvokableStmt callStmt, + SootMethod callee, + Kind kind, + AllocNode receiverNode) { Context tgtContext = pta.createCalleeCtx(caller, receiverNode, new CallSite(callStmt), callee); ContextMethod cstarget = pta.parameterize(callee, tgtContext); handleCallEdge(new Edge(caller, callStmt, cstarget, kind)); @@ -187,7 +192,7 @@ private void addVirtualEdge( } public void injectCallEdge(Object heapOrType, ContextMethod callee, Kind kind) { - Map stmtMap = + Map stmtMap = methodToInvokeStmt.computeIfAbsent(callee.method(), k -> DataFactory.createMap()); if (!stmtMap.containsKey(heapOrType)) { AbstractInvokeExpr ie = @@ -203,7 +208,8 @@ public void injectCallEdge(Object heapOrType, ContextMethod callee, Kind kind) { } } - public void addStaticEdge(ContextMethod caller, Stmt callStmt, SootMethod calleem, Kind kind) { + public void addStaticEdge( + ContextMethod caller, InvokableStmt callStmt, SootMethod calleem, Kind kind) { Context typeContext = pta.createCalleeCtx(caller, null, new CallSite(callStmt), calleem); ContextMethod callee = pta.parameterize(calleem, typeContext); handleCallEdge(new Edge(caller, callStmt, callee, kind)); @@ -247,7 +253,7 @@ private void processCallAssign(Edge e) { MethodNodeFactory srcnf = srcmpag.nodeFactory(); MethodNodeFactory tgtnf = tgtmpag.nodeFactory(); SootMethod tgtmtd = tgtmpag.getMethod(); - AbstractInvokeExpr ie = s.getInvokeExpr(); + AbstractInvokeExpr ie = s.asInvokableStmt().getInvokeExpr().get(); // add arg --> param edges. int numArgs = ie.getArgCount(); for (int i = 0; i < numArgs; i++) { diff --git a/sootup.qilin/src/main/java/qilin/core/builder/MethodNodeFactory.java b/sootup.qilin/src/main/java/qilin/core/builder/MethodNodeFactory.java index 42acdc1918c..2f4fd470d55 100644 --- a/sootup.qilin/src/main/java/qilin/core/builder/MethodNodeFactory.java +++ b/sootup.qilin/src/main/java/qilin/core/builder/MethodNodeFactory.java @@ -51,6 +51,7 @@ import sootup.core.jimple.common.ref.JParameterRef; import sootup.core.jimple.common.ref.JStaticFieldRef; import sootup.core.jimple.common.ref.JThisRef; +import sootup.core.jimple.common.stmt.InvokableStmt; import sootup.core.jimple.common.stmt.JAssignStmt; import sootup.core.jimple.common.stmt.JIdentityStmt; import sootup.core.jimple.common.stmt.JReturnStmt; @@ -131,9 +132,9 @@ public Node getNode(Value v) { /** Adds the edges required for this statement to the graph. */ public final void handleStmt(Stmt s) { - if (s.containsInvokeExpr()) { - mpag.addCallStmt(s); - handleInvokeStmt(s); + if (s.isInvokableStmt() && s.asInvokableStmt().containsInvokeExpr()) { + mpag.addCallStmt(s.asInvokableStmt()); + handleInvokeStmt(s.asInvokableStmt()); } else { handleIntraStmt(s); } @@ -143,8 +144,8 @@ public final void handleStmt(Stmt s) { * Adds the edges required for this statement to the graph. Add throw stmt if the invoke method * throws an Exception. */ - protected void handleInvokeStmt(Stmt s) { - AbstractInvokeExpr ie = s.getInvokeExpr(); + protected void handleInvokeStmt(InvokableStmt s) { + AbstractInvokeExpr ie = s.getInvokeExpr().get(); int numArgs = ie.getArgCount(); for (int i = 0; i < numArgs; i++) { Value arg = ie.getArg(i); diff --git a/sootup.qilin/src/main/java/qilin/core/builder/callgraph/Edge.java b/sootup.qilin/src/main/java/qilin/core/builder/callgraph/Edge.java index 063527bfdcf..c1ac72ba839 100644 --- a/sootup.qilin/src/main/java/qilin/core/builder/callgraph/Edge.java +++ b/sootup.qilin/src/main/java/qilin/core/builder/callgraph/Edge.java @@ -8,7 +8,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.Stmt; +import sootup.core.jimple.common.stmt.InvokableStmt; import sootup.core.model.SootMethod; /** @@ -31,7 +31,7 @@ public final class Edge implements Invalidable { * The unit at which the call occurs; may be null for calls not occurring at a specific statement * (eg. calls in native code) */ - private Stmt srcUnit; + private InvokableStmt srcUnit; /** * The kind of edge. Note: kind should not be tested by other classes; instead, accessors such as @@ -41,15 +41,17 @@ public final class Edge implements Invalidable { private boolean invalid = false; - public Edge(ContextMethod src, Stmt srcUnit, ContextMethod tgt, Kind kind) { + public Edge(ContextMethod src, InvokableStmt srcUnit, ContextMethod tgt, Kind kind) { + this.src = src; this.srcUnit = srcUnit; this.tgt = tgt; this.kind = kind; } - public Edge(ContextMethod src, Stmt srcUnit, ContextMethod tgt) { - this.kind = ieToKind(srcUnit.getInvokeExpr()); + public Edge(ContextMethod src, InvokableStmt srcUnit, ContextMethod tgt) { + + this.kind = ieToKind(srcUnit.getInvokeExpr().get()); this.src = src; this.srcUnit = srcUnit; this.tgt = tgt; @@ -67,11 +69,11 @@ public ContextMethod getSrc() { return src; } - public Stmt srcUnit() { + public InvokableStmt srcUnit() { return srcUnit; } - public Stmt srcStmt() { + public InvokableStmt srcStmt() { return srcUnit; } diff --git a/sootup.qilin/src/main/java/qilin/core/builder/callgraph/OnFlyCallGraph.java b/sootup.qilin/src/main/java/qilin/core/builder/callgraph/OnFlyCallGraph.java index cb430b9a4c7..250c3a39891 100644 --- a/sootup.qilin/src/main/java/qilin/core/builder/callgraph/OnFlyCallGraph.java +++ b/sootup.qilin/src/main/java/qilin/core/builder/callgraph/OnFlyCallGraph.java @@ -28,8 +28,10 @@ import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedHashSet; +import java.util.List; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; import javax.annotation.Nonnull; import qilin.core.pag.ContextMethod; import qilin.util.DataFactory; @@ -38,6 +40,7 @@ import sootup.callgraph.CallGraph; import sootup.callgraph.CallGraphDifference; import sootup.callgraph.MutableCallGraph; +import sootup.core.jimple.common.stmt.InvokableStmt; import sootup.core.jimple.common.stmt.Stmt; import sootup.core.model.SootMethod; import sootup.core.signatures.MethodSignature; @@ -51,7 +54,7 @@ */ public class OnFlyCallGraph implements MutableCallGraph, Iterable { protected Set methods = DataFactory.createSet(); - protected Map> calls = DataFactory.createMap(); + protected Map> calls = DataFactory.createMap(); protected int callCnt = 0; protected Set edges = new LinkedHashSet<>(); @@ -71,7 +74,7 @@ public boolean addEdge(Edge e) { MethodSignature tgtSig = e.getTgt().method().getSignature(); addMethod(srcSig); addMethod(tgtSig); - addCall(srcSig, tgtSig); + addCall(srcSig, tgtSig, e.srcStmt()); stream.add(e); Edge position = srcUnitToEdge.get(e.srcUnit()); @@ -131,7 +134,7 @@ public boolean removeAllEdgesOutOf(Stmt u) { * @param in The new statement * @return True if at least one edge was affected by this operation */ - public boolean swapEdgesOutOf(Stmt out, Stmt in) { + public boolean swapEdgesOutOf(InvokableStmt out, InvokableStmt in) { boolean hasSwapped = false; for (Iterator edgeRdr = edgesOutOf(out); edgeRdr.hasNext(); ) { Edge e = edgeRdr.next(); @@ -168,7 +171,7 @@ public boolean removeEdge(Edge e, boolean removeInEdgeList) { } MethodSignature srcSig = e.getSrc().method().getSignature(); MethodSignature tgtSig = e.getTgt().method().getSignature(); - Set tgtSigs = calls.getOrDefault(srcSig, Collections.emptySet()); + Set tgtSigs = calls.getOrDefault(srcSig, Collections.emptySet()); assert (!tgtSigs.isEmpty()); tgtSigs.remove(tgtSig); // !FIXME only edge is removed. I do not remove the added nodes. @@ -432,10 +435,11 @@ public void addMethod(@Nonnull MethodSignature calledMethod) { @Override public void addCall( - @Nonnull MethodSignature sourceMethod, @Nonnull MethodSignature targetMethod) { - Set targets = - this.calls.computeIfAbsent(sourceMethod, k -> DataFactory.createSet()); - if (targets.add(targetMethod)) { + @Nonnull MethodSignature sourceMethod, + @Nonnull MethodSignature targetMethod, + @Nonnull InvokableStmt stmt) { + Set targets = this.calls.computeIfAbsent(sourceMethod, k -> DataFactory.createSet()); + if (targets.add(new Call(sourceMethod, targetMethod, stmt))) { ++callCnt; } } @@ -465,9 +469,16 @@ public boolean containsMethod(@Nonnull MethodSignature method) { @Override public boolean containsCall( - @Nonnull MethodSignature sourceMethod, @Nonnull MethodSignature targetMethod) { - if (this.calls.containsKey(sourceMethod)) { - if (this.calls.get(sourceMethod).contains(targetMethod)) { + @Nonnull MethodSignature sourceMethod, + @Nonnull MethodSignature targetMethod, + InvokableStmt stmt) { + return containsCall(new Call(sourceMethod, targetMethod, stmt)); + } + + @Override + public boolean containsCall(@Nonnull Call call) { + if (this.calls.containsKey(call.getSourceMethodSignature())) { + if (this.calls.get(call.getSourceMethodSignature()).contains(call)) { return true; } } @@ -486,13 +497,35 @@ public String exportAsDot() { @Nonnull @Override - public Set callsFrom(@Nonnull MethodSignature sourceMethod) { + public Set callsFrom(@Nonnull MethodSignature sourceMethod) { return this.calls.getOrDefault(sourceMethod, Collections.emptySet()); } @Nonnull @Override - public Set callsTo(@Nonnull MethodSignature targetMethod) { + public Set callsTo(@Nonnull MethodSignature targetMethod) { throw new UnsupportedOperationException(); } + + @Nonnull + @Override + public Set callTargetsFrom(@Nonnull MethodSignature sourceMethod) { + return callsFrom(sourceMethod).stream() + .map(call -> call.getTargetMethodSignature()) + .collect(Collectors.toSet()); + } + + @Nonnull + @Override + public Set callSourcesTo(@Nonnull MethodSignature targetMethod) { + return callsTo(targetMethod).stream() + .map(call -> call.getSourceMethodSignature()) + .collect(Collectors.toSet()); + } + + // TODO: implement me + @Override + public List getEntryMethods() { + return Collections.emptyList(); + } } diff --git a/sootup.qilin/src/main/java/qilin/core/pag/CallSite.java b/sootup.qilin/src/main/java/qilin/core/pag/CallSite.java index c2c7e742b9c..f776279e0a5 100644 --- a/sootup.qilin/src/main/java/qilin/core/pag/CallSite.java +++ b/sootup.qilin/src/main/java/qilin/core/pag/CallSite.java @@ -19,18 +19,18 @@ package qilin.core.pag; import qilin.core.context.ContextElement; -import sootup.core.jimple.common.stmt.Stmt; +import sootup.core.jimple.common.stmt.InvokableStmt; /** callsite based context element in the points to analysis. */ public class CallSite implements ContextElement { - private final Stmt unit; + private final InvokableStmt unit; - public CallSite(Stmt unit) { + public CallSite(InvokableStmt unit) { this.unit = unit; } - public Stmt getUnit() { + public InvokableStmt getUnit() { return unit; } diff --git a/sootup.qilin/src/main/java/qilin/core/pag/MethodPAG.java b/sootup.qilin/src/main/java/qilin/core/pag/MethodPAG.java index 6c770f31ce4..f0da201b02e 100644 --- a/sootup.qilin/src/main/java/qilin/core/pag/MethodPAG.java +++ b/sootup.qilin/src/main/java/qilin/core/pag/MethodPAG.java @@ -29,6 +29,7 @@ import sootup.core.jimple.Jimple; import sootup.core.jimple.basic.Trap; import sootup.core.jimple.common.ref.JStaticFieldRef; +import sootup.core.jimple.common.stmt.InvokableStmt; import sootup.core.jimple.common.stmt.Stmt; import sootup.core.model.Body; import sootup.core.model.SootClass; @@ -44,7 +45,7 @@ public class MethodPAG { private final ChunkedQueue internalEdges = new ChunkedQueue<>(); private final QueueReader internalReader = internalEdges.reader(); private final Set clinits = DataFactory.createSet(); - private final Collection invokeStmts = DataFactory.createSet(); + private final Collection invokeStmts = DataFactory.createSet(); public Body body; /** @@ -81,11 +82,11 @@ public MethodNodeFactory nodeFactory() { return nodeFactory; } - public Collection getInvokeStmts() { + public Collection getInvokeStmts() { return invokeStmts; } - public boolean addCallStmt(Stmt unit) { + public boolean addCallStmt(InvokableStmt unit) { return this.invokeStmts.add(unit); } diff --git a/sootup.qilin/src/main/java/qilin/core/pag/PAG.java b/sootup.qilin/src/main/java/qilin/core/pag/PAG.java index a3e6503a789..7d8274c8ee7 100644 --- a/sootup.qilin/src/main/java/qilin/core/pag/PAG.java +++ b/sootup.qilin/src/main/java/qilin/core/pag/PAG.java @@ -570,8 +570,8 @@ private void handleArrayCopy(SootMethod method) { Body.BodyBuilder builder = Body.builder(body, Collections.emptySet()); int localCount = body.getLocalCount(); for (Stmt s : body.getStmts()) { - if (s.containsInvokeExpr()) { - AbstractInvokeExpr invokeExpr = s.getInvokeExpr(); + if (s.isInvokableStmt() && s.asInvokableStmt().containsInvokeExpr()) { + AbstractInvokeExpr invokeExpr = s.asInvokableStmt().getInvokeExpr().get(); if (invokeExpr instanceof JStaticInvokeExpr) { JStaticInvokeExpr sie = (JStaticInvokeExpr) invokeExpr; String sig = sie.getMethodSignature().toString(); diff --git a/sootup.qilin/src/main/java/qilin/core/pag/VirtualCallSite.java b/sootup.qilin/src/main/java/qilin/core/pag/VirtualCallSite.java index 42696812805..b985cdc1df7 100644 --- a/sootup.qilin/src/main/java/qilin/core/pag/VirtualCallSite.java +++ b/sootup.qilin/src/main/java/qilin/core/pag/VirtualCallSite.java @@ -21,7 +21,7 @@ import java.util.Objects; import qilin.core.builder.callgraph.Kind; import sootup.core.jimple.common.expr.AbstractInstanceInvokeExpr; -import sootup.core.jimple.common.stmt.Stmt; +import sootup.core.jimple.common.stmt.InvokableStmt; import sootup.core.signatures.MethodSubSignature; /** @@ -38,7 +38,7 @@ public class VirtualCallSite extends CallSite { public VirtualCallSite( VarNode recNode, - Stmt stmt, + InvokableStmt stmt, ContextMethod container, AbstractInstanceInvokeExpr iie, MethodSubSignature subSig, diff --git a/sootup.qilin/src/main/java/qilin/core/reflection/NopReflectionModel.java b/sootup.qilin/src/main/java/qilin/core/reflection/NopReflectionModel.java index 59be216aba2..0659b77c5a6 100644 --- a/sootup.qilin/src/main/java/qilin/core/reflection/NopReflectionModel.java +++ b/sootup.qilin/src/main/java/qilin/core/reflection/NopReflectionModel.java @@ -21,6 +21,7 @@ import java.util.Collection; import java.util.Collections; import qilin.core.PTAScene; +import sootup.core.jimple.common.stmt.InvokableStmt; import sootup.core.jimple.common.stmt.Stmt; /* @@ -34,47 +35,47 @@ public NopReflectionModel(PTAScene scene) { } @Override - Collection transformClassForName(Stmt s) { + Collection transformClassForName(InvokableStmt s) { return Collections.emptySet(); } @Override - Collection transformClassNewInstance(Stmt s) { + Collection transformClassNewInstance(InvokableStmt s) { return Collections.emptySet(); } @Override - Collection transformContructorNewInstance(Stmt s) { + Collection transformConstructorNewInstance(InvokableStmt s) { return Collections.emptySet(); } @Override - Collection transformMethodInvoke(Stmt s) { + Collection transformMethodInvoke(InvokableStmt s) { return Collections.emptySet(); } @Override - Collection transformFieldSet(Stmt s) { + Collection transformFieldSet(InvokableStmt s) { return Collections.emptySet(); } @Override - Collection transformFieldGet(Stmt s) { + Collection transformFieldGet(InvokableStmt s) { return Collections.emptySet(); } @Override - Collection transformArrayNewInstance(Stmt s) { + Collection transformArrayNewInstance(InvokableStmt s) { return Collections.emptySet(); } @Override - Collection transformArrayGet(Stmt s) { + Collection transformArrayGet(InvokableStmt s) { return Collections.emptySet(); } @Override - Collection transformArraySet(Stmt s) { + Collection transformArraySet(InvokableStmt s) { return Collections.emptySet(); } } diff --git a/sootup.qilin/src/main/java/qilin/core/reflection/ReflectionModel.java b/sootup.qilin/src/main/java/qilin/core/reflection/ReflectionModel.java index a4f2faa108a..bfffa990954 100644 --- a/sootup.qilin/src/main/java/qilin/core/reflection/ReflectionModel.java +++ b/sootup.qilin/src/main/java/qilin/core/reflection/ReflectionModel.java @@ -28,6 +28,7 @@ import sootup.core.graph.MutableStmtGraph; import sootup.core.jimple.common.expr.AbstractInvokeExpr; import sootup.core.jimple.common.stmt.FallsThroughStmt; +import sootup.core.jimple.common.stmt.InvokableStmt; import sootup.core.jimple.common.stmt.JAssignStmt; import sootup.core.jimple.common.stmt.JInvokeStmt; import sootup.core.jimple.common.stmt.Stmt; @@ -76,8 +77,8 @@ protected ReflectionModel(PTAScene ptaScene) { this.ptaScene = ptaScene; } - private Collection transform(Stmt s) { - AbstractInvokeExpr ie = s.getInvokeExpr(); + private Collection transform(InvokableStmt s) { + AbstractInvokeExpr ie = s.getInvokeExpr().get(); switch (ie.getMethodSignature().toString()) { case sigForName: case sigForName2: @@ -85,7 +86,7 @@ private Collection transform(Stmt s) { case sigClassNewInstance: return transformClassNewInstance(s); case sigConstructorNewInstance: - return transformContructorNewInstance(s); + return transformConstructorNewInstance(s); case sigMethodInvoke: return transformMethodInvoke(s); case sigFieldSet: @@ -112,8 +113,8 @@ public void buildReflection(SootMethod m) { Body body = PTAUtils.getMethodBody(m); List units = body.getStmts(); for (final Stmt u : units) { - if (u.containsInvokeExpr()) { - newUnits.put(u, transform(u)); + if (u.isInvokableStmt() && u.asInvokableStmt().containsInvokeExpr()) { + newUnits.put(u, transform(u.asInvokableStmt())); } } Body.BodyBuilder builder = Body.builder(body, Collections.emptySet()); @@ -136,21 +137,21 @@ public void buildReflection(SootMethod m) { PTAUtils.updateMethodBody(m, builder.build()); } - abstract Collection transformClassForName(Stmt s); + abstract Collection transformClassForName(InvokableStmt s); - abstract Collection transformClassNewInstance(Stmt s); + abstract Collection transformClassNewInstance(InvokableStmt s); - abstract Collection transformContructorNewInstance(Stmt s); + abstract Collection transformConstructorNewInstance(InvokableStmt s); - abstract Collection transformMethodInvoke(Stmt s); + abstract Collection transformMethodInvoke(InvokableStmt s); - abstract Collection transformFieldSet(Stmt s); + abstract Collection transformFieldSet(InvokableStmt s); - abstract Collection transformFieldGet(Stmt s); + abstract Collection transformFieldGet(InvokableStmt s); - abstract Collection transformArrayNewInstance(Stmt s); + abstract Collection transformArrayNewInstance(InvokableStmt s); - abstract Collection transformArrayGet(Stmt s); + abstract Collection transformArrayGet(InvokableStmt s); - abstract Collection transformArraySet(Stmt s); + abstract Collection transformArraySet(InvokableStmt s); } diff --git a/sootup.qilin/src/main/java/qilin/core/reflection/TamiflexModel.java b/sootup.qilin/src/main/java/qilin/core/reflection/TamiflexModel.java index 917a338531a..99b7f2af88d 100644 --- a/sootup.qilin/src/main/java/qilin/core/reflection/TamiflexModel.java +++ b/sootup.qilin/src/main/java/qilin/core/reflection/TamiflexModel.java @@ -40,6 +40,7 @@ import sootup.core.jimple.common.expr.JVirtualInvokeExpr; import sootup.core.jimple.common.ref.JArrayRef; 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.JInvokeStmt; import sootup.core.jimple.common.stmt.Stmt; @@ -68,7 +69,7 @@ public TamiflexModel(PTAScene ptaScene) { } @Override - Collection transformClassForName(Stmt s) { + Collection transformClassForName(InvokableStmt s) { // // Collection ret = DataFactory.createSet(); @@ -94,7 +95,7 @@ public static String dot2slashStyle(String clazz) { } @Override - protected Collection transformClassNewInstance(Stmt s) { + protected Collection transformClassNewInstance(InvokableStmt s) { // if (!(s instanceof JAssignStmt)) { return Collections.emptySet(); @@ -126,7 +127,7 @@ protected Collection transformClassNewInstance(Stmt s) { } @Override - protected Collection transformContructorNewInstance(Stmt s) { + protected Collection transformConstructorNewInstance(InvokableStmt s) { // if (!(s instanceof JAssignStmt)) { return Collections.emptySet(); @@ -137,7 +138,7 @@ protected Collection transformContructorNewInstance(Stmt s) { reflectionMap.getOrDefault(ReflectionKind.ConstructorNewInstance, Collections.emptyMap()); if (constructorNewInstances.containsKey(s)) { Collection constructorSignatures = constructorNewInstances.get(s); - AbstractInvokeExpr iie = s.getInvokeExpr(); + AbstractInvokeExpr iie = s.asInvokableStmt().getInvokeExpr().get(); Value args = iie.getArg(0); JArrayRef arrayRef = JavaJimple.getInstance().newArrayRef((Local) args, IntConstant.getInstance(0)); @@ -163,14 +164,14 @@ protected Collection transformContructorNewInstance(Stmt s) { } @Override - protected Collection transformMethodInvoke(Stmt s) { + protected Collection transformMethodInvoke(InvokableStmt s) { // Collection ret = DataFactory.createSet(); Map> methodInvokes = reflectionMap.getOrDefault(ReflectionKind.MethodInvoke, Collections.emptyMap()); if (methodInvokes.containsKey(s)) { Collection methodSignatures = methodInvokes.get(s); - AbstractInvokeExpr iie = s.getInvokeExpr(); + AbstractInvokeExpr iie = s.getInvokeExpr().get(); Value base = iie.getArg(0); Value args = iie.getArg(1); Local arg = null; @@ -213,14 +214,14 @@ protected Collection transformMethodInvoke(Stmt s) { } @Override - protected Collection transformFieldSet(Stmt s) { + protected Collection transformFieldSet(InvokableStmt s) { // Collection ret = DataFactory.createSet(); Map> fieldSets = reflectionMap.getOrDefault(ReflectionKind.FieldSet, Collections.emptyMap()); if (fieldSets.containsKey(s)) { Collection fieldSignatures = fieldSets.get(s); - AbstractInvokeExpr iie = s.getInvokeExpr(); + AbstractInvokeExpr iie = s.getInvokeExpr().get(); Value base = iie.getArg(0); Value rValue = iie.getArg(1); for (String fieldSignature : fieldSignatures) { @@ -243,7 +244,7 @@ protected Collection transformFieldSet(Stmt s) { } @Override - protected Collection transformFieldGet(Stmt s) { + protected Collection transformFieldGet(InvokableStmt s) { // Collection ret = DataFactory.createSet(); Map> fieldGets = @@ -251,7 +252,7 @@ protected Collection transformFieldGet(Stmt s) { if (fieldGets.containsKey(s) && s instanceof JAssignStmt) { Collection fieldSignatures = fieldGets.get(s); LValue lvalue = ((JAssignStmt) s).getLeftOp(); - AbstractInvokeExpr iie = s.getInvokeExpr(); + AbstractInvokeExpr iie = s.getInvokeExpr().get(); Value base = iie.getArg(0); for (String fieldSignature : fieldSignatures) { FieldSignature fieldSig = @@ -275,7 +276,7 @@ protected Collection transformFieldGet(Stmt s) { } @Override - protected Collection transformArrayNewInstance(Stmt s) { + protected Collection transformArrayNewInstance(InvokableStmt s) { // Collection ret = DataFactory.createSet(); Map> mappedToArrayTypes = @@ -294,9 +295,9 @@ protected Collection transformArrayNewInstance(Stmt s) { } @Override - Collection transformArrayGet(Stmt s) { + Collection transformArrayGet(InvokableStmt s) { Collection ret = DataFactory.createSet(); - AbstractInvokeExpr iie = s.getInvokeExpr(); + AbstractInvokeExpr iie = s.getInvokeExpr().get(); Value base = iie.getArg(0); if (s instanceof JAssignStmt) { LValue lvalue = ((JAssignStmt) s).getLeftOp(); @@ -319,9 +320,9 @@ Collection transformArrayGet(Stmt s) { } @Override - Collection transformArraySet(Stmt s) { + Collection transformArraySet(InvokableStmt s) { Collection ret = DataFactory.createSet(); - AbstractInvokeExpr iie = s.getInvokeExpr(); + AbstractInvokeExpr iie = s.getInvokeExpr().get(); Value base = iie.getArg(0); if (base.getType() instanceof ArrayType) { Value from = iie.getArg(2); @@ -436,8 +437,9 @@ private Collection inferSourceStmt( for (SootMethod sm : sourceMethods) { Body body = PTAUtils.getMethodBody(sm); for (Stmt stmt : body.getStmts()) { - if (stmt.containsInvokeExpr()) { - String methodSig = stmt.getInvokeExpr().getMethodSignature().toString(); + if (stmt.isInvokableStmt() && stmt.asInvokableStmt().containsInvokeExpr()) { + String methodSig = + stmt.asInvokableStmt().getInvokeExpr().get().getMethodSignature().toString(); if (matchReflectionKind(kind, methodSig)) { potential.add(stmt); } diff --git a/sootup.qilin/src/main/java/qilin/core/solver/Solver.java b/sootup.qilin/src/main/java/qilin/core/solver/Solver.java index d2008d28c09..59c10a84afe 100644 --- a/sootup.qilin/src/main/java/qilin/core/solver/Solver.java +++ b/sootup.qilin/src/main/java/qilin/core/solver/Solver.java @@ -38,6 +38,7 @@ import sootup.core.jimple.common.expr.AbstractInstanceInvokeExpr; import sootup.core.jimple.common.expr.AbstractInvokeExpr; import sootup.core.jimple.common.expr.JDynamicInvokeExpr; +import sootup.core.jimple.common.stmt.InvokableStmt; import sootup.core.jimple.common.stmt.JThrowStmt; import sootup.core.jimple.common.stmt.Stmt; import sootup.core.model.SootMethod; @@ -132,10 +133,10 @@ public void processStmts(Iterator newRMs) { } } - private void recordCallStmts(ContextMethod m, Collection units) { - for (final Stmt s : units) { + private void recordCallStmts(ContextMethod m, Collection units) { + for (final InvokableStmt s : units) { if (s.containsInvokeExpr()) { - AbstractInvokeExpr ie = s.getInvokeExpr(); + AbstractInvokeExpr ie = s.getInvokeExpr().get(); if (ie instanceof AbstractInstanceInvokeExpr) { AbstractInstanceInvokeExpr iie = (AbstractInstanceInvokeExpr) ie; Local receiver = iie.getBase(); @@ -173,7 +174,7 @@ private void recordThrowStmts(ContextMethod m, Collection stmts) { MethodPAG mpag = pag.getMethodPAG(sm); MethodNodeFactory nodeFactory = mpag.nodeFactory(); Node src; - if (stmt.containsInvokeExpr()) { + if (stmt.isInvokableStmt() && stmt.asInvokableStmt().containsInvokeExpr()) { src = nodeFactory.makeInvokeStmtThrowVarNode(stmt, sm); } else { assert stmt instanceof JThrowStmt; diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/conch/Conch.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/conch/Conch.java index 43e4694746c..83b270ecafa 100644 --- a/sootup.qilin/src/main/java/qilin/pta/toolkits/conch/Conch.java +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/conch/Conch.java @@ -29,6 +29,7 @@ import sootup.core.jimple.basic.Value; import sootup.core.jimple.common.expr.AbstractInvokeExpr; import sootup.core.jimple.common.expr.JSpecialInvokeExpr; +import sootup.core.jimple.common.stmt.InvokableStmt; import sootup.core.jimple.common.stmt.JInvokeStmt; import sootup.core.jimple.common.stmt.Stmt; import sootup.core.model.SootMethod; @@ -70,7 +71,7 @@ private SootMethod findInvokedConstructorOf(AllocNode heap) { for (Stmt unit : cmpag.getInvokeStmts()) { if (unit instanceof JInvokeStmt) { JInvokeStmt invokeStmt = (JInvokeStmt) unit; - AbstractInvokeExpr expr = invokeStmt.getInvokeExpr(); + AbstractInvokeExpr expr = invokeStmt.getInvokeExpr().get(); if (expr instanceof JSpecialInvokeExpr) { JSpecialInvokeExpr iie = (JSpecialInvokeExpr) expr; Value base = iie.getBase(); @@ -95,7 +96,7 @@ private SootMethod findInvokedConstructorOf(SootMethod outerInit) { for (Stmt unit : cmpag.getInvokeStmts()) { if (unit instanceof JInvokeStmt) { JInvokeStmt invokeStmt = (JInvokeStmt) unit; - AbstractInvokeExpr expr = invokeStmt.getInvokeExpr(); + AbstractInvokeExpr expr = invokeStmt.getInvokeExpr().get(); if (expr instanceof JSpecialInvokeExpr) { JSpecialInvokeExpr iie = (JSpecialInvokeExpr) expr; Value base = iie.getBase(); @@ -131,11 +132,11 @@ private Set mappingtoCallerCommingParamsOrHeaps( Set params, SootMethod curr, SootMethod caller) { MethodPAG cmpag = pag.getMethodPAG(caller); Set ret = new HashSet<>(); - for (Stmt stmt : cmpag.getInvokeStmts()) { - if (!(stmt.getInvokeExpr() instanceof JSpecialInvokeExpr)) { + for (InvokableStmt stmt : cmpag.getInvokeStmts()) { + if (!(stmt.getInvokeExpr().get() instanceof JSpecialInvokeExpr)) { continue; } - MethodSignature methodSig = stmt.getInvokeExpr().getMethodSignature(); + MethodSignature methodSig = stmt.getInvokeExpr().get().getMethodSignature(); Optional otarget = pta.getView().getMethod(methodSig); if (otarget.isPresent() && otarget.get().equals(curr)) { for (Node n : params) { diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/conch/DepOnParamAnalysis.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/conch/DepOnParamAnalysis.java index a391de9be6d..c4c4ebb9f47 100644 --- a/sootup.qilin/src/main/java/qilin/pta/toolkits/conch/DepOnParamAnalysis.java +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/conch/DepOnParamAnalysis.java @@ -28,8 +28,8 @@ import qilin.core.builder.callgraph.Edge; import qilin.core.pag.*; import qilin.util.PTAUtils; +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.SootMethod; /* @@ -119,7 +119,7 @@ public void run() { SootMethod srcMethod = edge.src(); MethodPAG srcmpag = prePAG.getMethodPAG(srcMethod); MethodNodeFactory srcnf = srcmpag.nodeFactory(); - Stmt invokeStmt = edge.srcUnit(); + InvokableStmt invokeStmt = edge.srcUnit(); if (invokeStmt instanceof JAssignStmt) { JAssignStmt assignStmt = (JAssignStmt) invokeStmt; diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/conch/LeakAnalysis.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/conch/LeakAnalysis.java index b621ac9dcc8..29c58e507e7 100644 --- a/sootup.qilin/src/main/java/qilin/pta/toolkits/conch/LeakAnalysis.java +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/conch/LeakAnalysis.java @@ -27,7 +27,7 @@ import qilin.core.builder.callgraph.Edge; import qilin.core.pag.*; import qilin.util.PTAUtils; -import sootup.core.jimple.common.stmt.Stmt; +import sootup.core.jimple.common.stmt.InvokableStmt; import sootup.core.model.SootMethod; /* @@ -133,7 +133,7 @@ public void run() { Edge edge = it.next(); SootMethod srcMethod = edge.src(); MethodPAG srcmpag = prePAG.getMethodPAG(srcMethod); - Stmt invokeStmt = edge.srcUnit(); + InvokableStmt invokeStmt = edge.srcUnit(); if (targetState == DFA.State.F) { // ret.f* = heap // add S -new-> r summary edge for symbolic heaps. VarNode ret = (VarNode) targetNode; @@ -154,7 +154,7 @@ public void run() { Edge edge = it.next(); SootMethod srcMethod = edge.src(); MethodPAG srcmpag = prePAG.getMethodPAG(srcMethod); - Stmt invokeStmt = edge.srcUnit(); + InvokableStmt invokeStmt = edge.srcUnit(); VarNode aj = PTAUtils.paramToArg(prePAG, invokeStmt, srcmpag, pj); // a param reach end state. if (targetState == DFA.State.B && sourceNode != targetNode) { // pi.f* = pj, pi != pj. @@ -185,7 +185,7 @@ public void run() { Edge edge = it.next(); SootMethod srcMethod = edge.src(); MethodPAG srcmpag = prePAG.getMethodPAG(srcMethod); - Stmt invokeStmt = edge.srcUnit(); + InvokableStmt invokeStmt = edge.srcUnit(); VarNode ai = PTAUtils.paramToArg(prePAG, invokeStmt, srcmpag, pi); VarNode r = PTAUtils.paramToArg(prePAG, invokeStmt, srcmpag, retOrThrow); if (r != null && ai != null) { diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/dd/CtxTunnelingFeaturesTrueTable.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/dd/CtxTunnelingFeaturesTrueTable.java index 61f15ebeb30..1f380c63f7c 100644 --- a/sootup.qilin/src/main/java/qilin/pta/toolkits/dd/CtxTunnelingFeaturesTrueTable.java +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/dd/CtxTunnelingFeaturesTrueTable.java @@ -94,7 +94,7 @@ public CtxTunnelingFeaturesTrueTable(View view, SootMethod sm) { } } else if (unit instanceof JInvokeStmt) { JInvokeStmt invokeStmt = (JInvokeStmt) unit; - AbstractInvokeExpr expr = invokeStmt.getInvokeExpr(); + AbstractInvokeExpr expr = invokeStmt.asInvokableStmt().getInvokeExpr().get(); if (expr instanceof JStaticInvokeExpr) { this.f[17] = true; } else if (expr instanceof JVirtualInvokeExpr || expr instanceof JInterfaceInvokeExpr) { diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/debloaterx/IntraFlowAnalysis.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/debloaterx/IntraFlowAnalysis.java index e6fa96f8c29..ec5f37e3d90 100644 --- a/sootup.qilin/src/main/java/qilin/pta/toolkits/debloaterx/IntraFlowAnalysis.java +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/debloaterx/IntraFlowAnalysis.java @@ -14,7 +14,7 @@ import sootup.core.jimple.common.constant.NullConstant; import sootup.core.jimple.common.expr.AbstractInstanceInvokeExpr; import sootup.core.jimple.common.expr.AbstractInvokeExpr; -import sootup.core.jimple.common.stmt.Stmt; +import sootup.core.jimple.common.stmt.InvokableStmt; import sootup.core.model.SootMethod; import sootup.core.signatures.MethodSubSignature; import sootup.core.types.ArrayType; @@ -142,8 +142,8 @@ private Set collectParamInArguments(AllocNode heap) { HeapContainerQuery hcq = this.utility.getHCQ(heap); Set inParams = hcq.getInParamsToCSFields(); MethodPAG srcmpag = pag.getMethodPAG(method); - for (final Stmt s : srcmpag.getInvokeStmts()) { - AbstractInvokeExpr ie = s.getInvokeExpr(); + for (final InvokableStmt s : srcmpag.getInvokeStmts()) { + AbstractInvokeExpr ie = s.getInvokeExpr().get(); if (!(ie instanceof AbstractInstanceInvokeExpr)) { continue; } diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/debloaterx/XPAG.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/debloaterx/XPAG.java index 5969fa91ec6..d63b7166b7c 100644 --- a/sootup.qilin/src/main/java/qilin/pta/toolkits/debloaterx/XPAG.java +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/debloaterx/XPAG.java @@ -14,8 +14,8 @@ import sootup.core.jimple.common.expr.AbstractInvokeExpr; import sootup.core.jimple.common.expr.JSpecialInvokeExpr; import sootup.core.jimple.common.expr.JStaticInvokeExpr; +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.SootMethod; import sootup.core.types.ReferenceType; @@ -75,8 +75,8 @@ protected void buildInternalWithInline(SootMethod method) { } // global-local } // handle call statements. - for (final Stmt s : srcmpag.getInvokeStmts()) { - AbstractInvokeExpr ie = s.getInvokeExpr(); + for (final InvokableStmt s : srcmpag.getInvokeStmts()) { + AbstractInvokeExpr ie = s.getInvokeExpr().get(); int numArgs = ie.getArgCount(); Value[] args = new Value[numArgs]; for (int i = 0; i < numArgs; i++) { @@ -158,8 +158,8 @@ private void modelVirtualCall( addCStoreEdge(receiver, receiver); } - private void inline(SootMethod srcMethod, Stmt invokeStmt, SootMethod tgtMethod) { - AbstractInvokeExpr ie = invokeStmt.getInvokeExpr(); + private void inline(SootMethod srcMethod, InvokableStmt invokeStmt, SootMethod tgtMethod) { + AbstractInvokeExpr ie = invokeStmt.getInvokeExpr().get(); int numArgs = ie.getArgCount(); Value[] args = new Value[numArgs]; for (int i = 0; i < numArgs; i++) { diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/debloaterx/XUtility.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/debloaterx/XUtility.java index e1f9bdf5170..328fc10d10b 100644 --- a/sootup.qilin/src/main/java/qilin/pta/toolkits/debloaterx/XUtility.java +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/debloaterx/XUtility.java @@ -14,7 +14,7 @@ import sootup.core.jimple.basic.Local; import sootup.core.jimple.common.expr.AbstractInstanceInvokeExpr; import sootup.core.jimple.common.expr.AbstractInvokeExpr; -import sootup.core.jimple.common.stmt.Stmt; +import sootup.core.jimple.common.stmt.InvokableStmt; import sootup.core.model.SootClass; import sootup.core.model.SootField; import sootup.core.model.SootMethod; @@ -224,8 +224,8 @@ private void buildHeapMethodsMapping() { if (tgtM.isStatic() || !PTAUtils.hasBody(tgtM)) { continue; } - final Stmt s = edge.srcStmt(); - AbstractInvokeExpr ie = s.getInvokeExpr(); + final InvokableStmt s = edge.srcStmt(); + AbstractInvokeExpr ie = s.getInvokeExpr().get(); if (ie instanceof AbstractInstanceInvokeExpr) { AbstractInstanceInvokeExpr iie = (AbstractInstanceInvokeExpr) ie; Local base = iie.getBase(); diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/eagle/Eagle.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/eagle/Eagle.java index 8fb78dbbd30..50f51846249 100644 --- a/sootup.qilin/src/main/java/qilin/pta/toolkits/eagle/Eagle.java +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/eagle/Eagle.java @@ -36,8 +36,8 @@ import sootup.core.jimple.common.constant.NullConstant; import sootup.core.jimple.common.expr.AbstractInstanceInvokeExpr; import sootup.core.jimple.common.expr.AbstractInvokeExpr; +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.SootMethod; import sootup.core.types.ReferenceType; @@ -344,8 +344,8 @@ else if (to instanceof FieldRefNode) { } // add invoke edges - for (final Stmt s : srcmpag.getInvokeStmts()) { - AbstractInvokeExpr ie = s.getInvokeExpr(); + for (final InvokableStmt s : srcmpag.getInvokeStmts()) { + AbstractInvokeExpr ie = s.getInvokeExpr().get(); int numArgs = ie.getArgCount(); Value[] args = new Value[numArgs]; for (int i = 0; i < numArgs; i++) { diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/selectx/Selectx.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/selectx/Selectx.java index 4ca2eb37ff2..d1ae487089e 100644 --- a/sootup.qilin/src/main/java/qilin/pta/toolkits/selectx/Selectx.java +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/selectx/Selectx.java @@ -33,8 +33,8 @@ import sootup.core.jimple.common.constant.NullConstant; import sootup.core.jimple.common.expr.AbstractInstanceInvokeExpr; import sootup.core.jimple.common.expr.AbstractInvokeExpr; +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.SootMethod; import sootup.core.types.ReferenceType; @@ -295,9 +295,9 @@ private void buildGraph() { // add invoke edges MethodNodeFactory srcnf = srcmpag.nodeFactory(); - for (final Stmt s : srcmpag.getInvokeStmts()) { + for (final InvokableStmt s : srcmpag.getInvokeStmts()) { CallSite callSite = new CallSite(s); - AbstractInvokeExpr ie = s.getInvokeExpr(); + AbstractInvokeExpr ie = s.getInvokeExpr().get(); int numArgs = ie.getArgCount(); Value[] args = new Value[numArgs]; for (int i = 0; i < numArgs; i++) { diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/turner/AbstractMVFG.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/turner/AbstractMVFG.java index 1612242b541..4a2f1e0f41d 100644 --- a/sootup.qilin/src/main/java/qilin/pta/toolkits/turner/AbstractMVFG.java +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/turner/AbstractMVFG.java @@ -158,7 +158,7 @@ else if (to instanceof FieldRefNode) { // add invoke edges for (final Stmt s : srcmpag.getInvokeStmts()) { - AbstractInvokeExpr ie = s.getInvokeExpr(); + AbstractInvokeExpr ie = s.asInvokableStmt().getInvokeExpr().get(); int numArgs = ie.getArgCount(); Value[] args = new Value[numArgs]; for (int i = 0; i < numArgs; i++) { diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/zipper/flowgraph/FlowAnalysis.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/zipper/flowgraph/FlowAnalysis.java index e952716e526..e4466e833be 100644 --- a/sootup.qilin/src/main/java/qilin/pta/toolkits/zipper/flowgraph/FlowAnalysis.java +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/zipper/flowgraph/FlowAnalysis.java @@ -148,7 +148,7 @@ private void dfs(Node node) { .forEach( vcs -> { Stmt callsiteStmt = vcs.getUnit(); - AbstractInvokeExpr invo = callsiteStmt.getInvokeExpr(); + AbstractInvokeExpr invo = callsiteStmt.asInvokableStmt().getInvokeExpr().get(); if (!(invo instanceof AbstractInstanceInvokeExpr)) { return; } diff --git a/sootup.qilin/src/main/java/qilin/pta/toolkits/zipper/flowgraph/ObjectFlowGraph.java b/sootup.qilin/src/main/java/qilin/pta/toolkits/zipper/flowgraph/ObjectFlowGraph.java index f18e390a444..2ad061d02ae 100644 --- a/sootup.qilin/src/main/java/qilin/pta/toolkits/zipper/flowgraph/ObjectFlowGraph.java +++ b/sootup.qilin/src/main/java/qilin/pta/toolkits/zipper/flowgraph/ObjectFlowGraph.java @@ -8,7 +8,7 @@ import sootup.core.jimple.basic.Value; import sootup.core.jimple.common.expr.AbstractInstanceInvokeExpr; import sootup.core.jimple.common.expr.AbstractInvokeExpr; -import sootup.core.jimple.common.stmt.Stmt; +import sootup.core.jimple.common.stmt.InvokableStmt; import sootup.core.model.SootMethod; public class ObjectFlowGraph implements IObjectFlowGraph { @@ -112,14 +112,14 @@ private void init() { pta.getCallGraph() .forEach( e -> { - Stmt callsite = e.srcStmt(); + InvokableStmt callsite = e.srcStmt(); SootMethod caller = e.src(); if (caller != null) { SootMethod callee = e.tgt(); if (!callee.isStatic()) { MethodNodeFactory calleeNF = pta.getPag().getMethodPAG(callee).nodeFactory(); LocalVarNode thisVar = (LocalVarNode) calleeNF.caseThis(); - AbstractInvokeExpr ie = callsite.getInvokeExpr(); + AbstractInvokeExpr ie = callsite.getInvokeExpr().get(); Value base = null; if (ie instanceof AbstractInstanceInvokeExpr) { AbstractInstanceInvokeExpr iie = (AbstractInstanceInvokeExpr) ie; diff --git a/sootup.qilin/src/main/java/qilin/pta/tools/PartialCallSiteSensPTA.java b/sootup.qilin/src/main/java/qilin/pta/tools/PartialCallSiteSensPTA.java index 8e23047f4bf..91891403b69 100644 --- a/sootup.qilin/src/main/java/qilin/pta/tools/PartialCallSiteSensPTA.java +++ b/sootup.qilin/src/main/java/qilin/pta/tools/PartialCallSiteSensPTA.java @@ -36,8 +36,8 @@ import sootup.core.jimple.common.constant.NullConstant; import sootup.core.jimple.common.expr.AbstractInstanceInvokeExpr; import sootup.core.jimple.common.expr.AbstractInvokeExpr; +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.SootMethod; import sootup.core.types.ReferenceType; @@ -148,8 +148,8 @@ protected void extraStats() { } } } - for (final Stmt s : srcmpag.getInvokeStmts()) { - AbstractInvokeExpr ie = s.getInvokeExpr(); + for (final InvokableStmt s : srcmpag.getInvokeStmts()) { + AbstractInvokeExpr ie = s.getInvokeExpr().get(); int numArgs = ie.getArgCount(); for (int i = 0; i < numArgs; i++) { Value arg = ie.getArg(i); diff --git a/sootup.qilin/src/main/java/qilin/pta/tools/PartialObjSensPTA.java b/sootup.qilin/src/main/java/qilin/pta/tools/PartialObjSensPTA.java index 20029388eb6..0b89aba749f 100644 --- a/sootup.qilin/src/main/java/qilin/pta/tools/PartialObjSensPTA.java +++ b/sootup.qilin/src/main/java/qilin/pta/tools/PartialObjSensPTA.java @@ -153,7 +153,7 @@ protected void extraStats() { } } for (final Stmt s : srcmpag.getInvokeStmts()) { - AbstractInvokeExpr ie = s.getInvokeExpr(); + AbstractInvokeExpr ie = s.asInvokableStmt().getInvokeExpr().get(); int numArgs = ie.getArgCount(); for (int i = 0; i < numArgs; i++) { Value arg = ie.getArg(i); diff --git a/sootup.qilin/src/main/java/qilin/pta/tools/ZipperPTA.java b/sootup.qilin/src/main/java/qilin/pta/tools/ZipperPTA.java index a239b1ed997..0c75c03bb21 100644 --- a/sootup.qilin/src/main/java/qilin/pta/tools/ZipperPTA.java +++ b/sootup.qilin/src/main/java/qilin/pta/tools/ZipperPTA.java @@ -39,8 +39,8 @@ import sootup.core.jimple.common.constant.NullConstant; import sootup.core.jimple.common.expr.AbstractInstanceInvokeExpr; import sootup.core.jimple.common.expr.AbstractInvokeExpr; +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.SootMethod; import sootup.core.types.ReferenceType; @@ -121,8 +121,8 @@ protected void extraStats() { } } } - for (final Stmt s : srcmpag.getInvokeStmts()) { - AbstractInvokeExpr ie = s.getInvokeExpr(); + for (final InvokableStmt s : srcmpag.getInvokeStmts()) { + AbstractInvokeExpr ie = s.getInvokeExpr().get(); int numArgs = ie.getArgCount(); for (int i = 0; i < numArgs; i++) { Value arg = ie.getArg(i); diff --git a/sootup.qilin/src/main/java/qilin/stat/SimplifiedEvaluator.java b/sootup.qilin/src/main/java/qilin/stat/SimplifiedEvaluator.java index 1b76a5ca652..69e06dc565f 100644 --- a/sootup.qilin/src/main/java/qilin/stat/SimplifiedEvaluator.java +++ b/sootup.qilin/src/main/java/qilin/stat/SimplifiedEvaluator.java @@ -81,8 +81,8 @@ public void end() { // All the statements in the method for (Stmt st : PTAUtils.getMethodBody(sm).getStmts()) { // virtual calls - if (st.containsInvokeExpr()) { - AbstractInvokeExpr ie = st.getInvokeExpr(); + if (st.isInvokableStmt() && st.asInvokableStmt().containsInvokeExpr()) { + AbstractInvokeExpr ie = st.asInvokableStmt().getInvokeExpr().get(); if (!(ie instanceof JStaticInvokeExpr)) { // Virtual, Special or Instance // have to check target soot method, cannot just diff --git a/sootup.qilin/src/main/java/qilin/stat/TypeClientStat.java b/sootup.qilin/src/main/java/qilin/stat/TypeClientStat.java index ae51e63f874..bfaba5c754b 100644 --- a/sootup.qilin/src/main/java/qilin/stat/TypeClientStat.java +++ b/sootup.qilin/src/main/java/qilin/stat/TypeClientStat.java @@ -85,8 +85,8 @@ private void init() { // All the statements in the method for (Stmt st : PTAUtils.getMethodBody(sm).getStmts()) { // virtual calls - if (st.containsInvokeExpr()) { - AbstractInvokeExpr ie = st.getInvokeExpr(); + if (st.isInvokableStmt() && st.asInvokableStmt().containsInvokeExpr()) { + AbstractInvokeExpr ie = st.asInvokableStmt().getInvokeExpr().get(); if (ie instanceof JStaticInvokeExpr) { totalStaticCalls++; } else { // Virtual, Special or Instance diff --git a/sootup.qilin/src/main/java/qilin/util/PTAUtils.java b/sootup.qilin/src/main/java/qilin/util/PTAUtils.java index d5392c5775c..bdfe0a1a79a 100644 --- a/sootup.qilin/src/main/java/qilin/util/PTAUtils.java +++ b/sootup.qilin/src/main/java/qilin/util/PTAUtils.java @@ -45,8 +45,8 @@ import sootup.core.jimple.common.expr.AbstractInvokeExpr; import sootup.core.jimple.common.expr.JNewArrayExpr; import sootup.core.jimple.common.expr.JStaticInvokeExpr; +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.Body; import sootup.core.model.SootClass; import sootup.core.model.SootMethod; @@ -89,8 +89,8 @@ public static Map> calcStaticThisPTS(PTA pta) { LocalVarNode thisRef = (LocalVarNode) srcmpag.nodeFactory().caseThis(); final PointsToSet other = pta.reachingObjects(thisRef).toCIPointsToSet(); - for (final Stmt s : srcmpag.getInvokeStmts()) { - AbstractInvokeExpr ie = s.getInvokeExpr(); + for (final InvokableStmt s : srcmpag.getInvokeStmts()) { + AbstractInvokeExpr ie = s.getInvokeExpr().get(); if (ie instanceof JStaticInvokeExpr) { for (Iterator it = pta.getCallGraph().edgesOutOf(s); it.hasNext(); ) { Edge e = it.next(); @@ -122,8 +122,8 @@ public static Map> calcStaticThisPTS(PTA pta) { LocalVarNode thisRef = (LocalVarNode) srcmpag.nodeFactory().caseThis(); final Set other = pts.computeIfAbsent(thisRef, k -> new HashSet<>()); - for (final Stmt s : srcmpag.getInvokeStmts()) { - AbstractInvokeExpr ie = s.getInvokeExpr(); + for (final InvokableStmt s : srcmpag.getInvokeStmts()) { + AbstractInvokeExpr ie = s.getInvokeExpr().get(); if (ie instanceof JStaticInvokeExpr) { for (Iterator it = pta.getCallGraph().edgesOutOf(s); it.hasNext(); ) { Edge e = it.next(); @@ -364,9 +364,10 @@ public static boolean isEmptyArray(AllocNode heap) { return false; } - public static LocalVarNode paramToArg(PAG pag, Stmt invokeStmt, MethodPAG srcmpag, VarNode pi) { + public static LocalVarNode paramToArg( + PAG pag, InvokableStmt invokeStmt, MethodPAG srcmpag, VarNode pi) { MethodNodeFactory srcnf = srcmpag.nodeFactory(); - AbstractInvokeExpr ie = invokeStmt.getInvokeExpr(); + AbstractInvokeExpr ie = invokeStmt.getInvokeExpr().get(); Parm mPi = (Parm) pi.getVariable(); LocalVarNode thisRef = (LocalVarNode) srcnf.caseThis(); LocalVarNode receiver; diff --git a/sootup.qilin/src/test/java/qilin/test/util/AssertionsParser.java b/sootup.qilin/src/test/java/qilin/test/util/AssertionsParser.java index 1bddbff2f90..dbbf668c7cd 100644 --- a/sootup.qilin/src/test/java/qilin/test/util/AssertionsParser.java +++ b/sootup.qilin/src/test/java/qilin/test/util/AssertionsParser.java @@ -50,8 +50,8 @@ public static Set retrieveQueryInfo(PTA pta) { // System.out.println("================================================================="); // } for (final Stmt stmt : PTAUtils.getMethodBody(sm).getStmts()) { - if (stmt.containsInvokeExpr()) { - AbstractInvokeExpr ie = stmt.getInvokeExpr(); + if (stmt.isInvokableStmt() && stmt.asInvokableStmt().containsInvokeExpr()) { + AbstractInvokeExpr ie = stmt.asInvokableStmt().getInvokeExpr().get(); if (ie instanceof JStaticInvokeExpr) { final MethodSignature calleeSig = ie.getMethodSignature(); if (calleeSig.toString().equals(mayAliasSig)) { diff --git a/sootup.tests/pom.xml b/sootup.tests/pom.xml index c84ff85765f..3289614075b 100644 --- a/sootup.tests/pom.xml +++ b/sootup.tests/pom.xml @@ -44,12 +44,6 @@ sootup.callgraph ${project.version} - - org.soot-oss - sootup.jimple.parser - 1.3.1-SNAPSHOT - test - diff --git a/sootup.tests/src/test/java/sootup/tests/CallGraphTest.java b/sootup.tests/src/test/java/sootup/tests/CallGraphTest.java index 109dbfa9a2e..63f02d032ce 100644 --- a/sootup.tests/src/test/java/sootup/tests/CallGraphTest.java +++ b/sootup.tests/src/test/java/sootup/tests/CallGraphTest.java @@ -12,6 +12,8 @@ import sootup.callgraph.ClassHierarchyAnalysisAlgorithm; import sootup.callgraph.RapidTypeAnalysisAlgorithm; import sootup.core.inputlocation.AnalysisInputLocation; +import sootup.core.jimple.common.stmt.InvokableStmt; +import sootup.core.jimple.common.stmt.Stmt; import sootup.core.model.SootClass; import sootup.core.model.SootMethod; import sootup.core.signatures.MethodSignature; @@ -28,6 +30,7 @@ public class CallGraphTest { protected JavaClassType mainClassSignature; protected MethodSignature mainMethodSignature; private String algorithmName; + private JavaView view; protected AbstractCallGraphAlgorithm createAlgorithm(JavaView view) { if (algorithmName.equals("RTA")) { @@ -54,7 +57,7 @@ CallGraph loadCallGraph() { String classPath = "src/test/resources/callgraph/" + "Misc"; // JavaView view = viewToClassPath.computeIfAbsent(classPath, this::createViewForClassPath); - JavaView view = createViewForClassPath(classPath); + view = createViewForClassPath(classPath); identifierFactory = view.getIdentifierFactory(); mainClassSignature = identifierFactory.getClassType("Main"); @@ -76,6 +79,24 @@ CallGraph loadCallGraph() { return cg; } + protected InvokableStmt getInvokableStmt( + MethodSignature sourceMethod, MethodSignature staticTargetMethod) { + SootMethod method = view.getMethod(sourceMethod).orElse(null); + assertNotNull(method); + for (Stmt stmt : method.getBody().getStmts()) { + if (stmt.isInvokableStmt() + && stmt.asInvokableStmt().containsInvokeExpr() + && stmt.asInvokableStmt() + .getInvokeExpr() + .get() + .getMethodSignature() + .equals(staticTargetMethod)) { + return stmt.asInvokableStmt(); + } + } + throw new RuntimeException("No invokable stmt found for " + sourceMethod); + } + @Test public void testRTA() { algorithmName = "RTA"; @@ -112,11 +133,31 @@ public void testRTA() { "int", Collections.emptyList()); - assertFalse(cg.containsCall(mainMethodSignature, methodAbstract)); - assertFalse(cg.containsCall(mainMethodSignature, methodMethodImplemented)); - assertTrue(cg.containsCall(mainMethodSignature, methodMethodImplementedInstantiatedInSubClass)); - assertFalse(cg.containsCall(mainMethodSignature, methodSubClassMethodNotImplemented)); - assertTrue(cg.containsCall(mainMethodSignature, methodSubClassMethodImplemented)); + assertFalse( + cg.containsCall( + mainMethodSignature, + methodAbstract, + getInvokableStmt(mainMethodSignature, methodAbstract))); + assertFalse( + cg.containsCall( + mainMethodSignature, + methodMethodImplemented, + getInvokableStmt(mainMethodSignature, methodAbstract))); + assertTrue( + cg.containsCall( + mainMethodSignature, + methodMethodImplementedInstantiatedInSubClass, + getInvokableStmt(mainMethodSignature, methodAbstract))); + assertFalse( + cg.containsCall( + mainMethodSignature, + methodSubClassMethodNotImplemented, + getInvokableStmt(mainMethodSignature, methodAbstract))); + assertTrue( + cg.containsCall( + mainMethodSignature, + methodSubClassMethodImplemented, + getInvokableStmt(mainMethodSignature, methodAbstract))); MethodSignature methodInterface = identifierFactory.getMethodSignature( @@ -143,10 +184,26 @@ public void testRTA() { "int", Collections.emptyList()); - assertTrue(cg.containsCall(mainMethodSignature, methodInterface)); - assertTrue(cg.containsCall(mainMethodSignature, methodInterfaceImplementation)); - assertFalse(cg.containsCall(mainMethodSignature, methodInterfaceNoImplementation)); - assertFalse(cg.containsCall(mainMethodSignature, methodInterfaceImplementationNotInstatiated)); + assertTrue( + cg.containsCall( + mainMethodSignature, + methodInterface, + getInvokableStmt(mainMethodSignature, methodInterface))); + assertTrue( + cg.containsCall( + mainMethodSignature, + methodInterfaceImplementation, + getInvokableStmt(mainMethodSignature, methodInterface))); + assertFalse( + cg.containsCall( + mainMethodSignature, + methodInterfaceNoImplementation, + getInvokableStmt(mainMethodSignature, methodInterface))); + assertFalse( + cg.containsCall( + mainMethodSignature, + methodInterfaceImplementationNotInstatiated, + getInvokableStmt(mainMethodSignature, methodInterface))); } @Test @@ -185,11 +242,31 @@ public void testCHA() { "int", Collections.emptyList()); - assertFalse(cg.containsCall(mainMethodSignature, methodAbstract)); - assertTrue(cg.containsCall(mainMethodSignature, methodMethodImplemented)); - assertTrue(cg.containsCall(mainMethodSignature, methodMethodImplementedInstantiatedInSubClass)); - assertFalse(cg.containsCall(mainMethodSignature, methodSubClassMethodNotImplemented)); - assertTrue(cg.containsCall(mainMethodSignature, methodSubClassMethodImplemented)); + assertFalse( + cg.containsCall( + mainMethodSignature, + methodAbstract, + getInvokableStmt(mainMethodSignature, methodAbstract))); + assertTrue( + cg.containsCall( + mainMethodSignature, + methodMethodImplemented, + getInvokableStmt(mainMethodSignature, methodAbstract))); + assertTrue( + cg.containsCall( + mainMethodSignature, + methodMethodImplementedInstantiatedInSubClass, + getInvokableStmt(mainMethodSignature, methodAbstract))); + assertFalse( + cg.containsCall( + mainMethodSignature, + methodSubClassMethodNotImplemented, + getInvokableStmt(mainMethodSignature, methodAbstract))); + assertTrue( + cg.containsCall( + mainMethodSignature, + methodSubClassMethodImplemented, + getInvokableStmt(mainMethodSignature, methodAbstract))); MethodSignature methodInterface = identifierFactory.getMethodSignature( @@ -216,10 +293,26 @@ public void testCHA() { "int", Collections.emptyList()); - assertTrue(cg.containsCall(mainMethodSignature, methodInterface)); - assertTrue(cg.containsCall(mainMethodSignature, methodInterfaceImplementation)); - assertFalse(cg.containsCall(mainMethodSignature, methodInterfaceNoImplementation)); - assertTrue(cg.containsCall(mainMethodSignature, methodInterfaceImplementationNotInstatiated)); + assertTrue( + cg.containsCall( + mainMethodSignature, + methodInterface, + getInvokableStmt(mainMethodSignature, methodInterface))); + assertTrue( + cg.containsCall( + mainMethodSignature, + methodInterfaceImplementation, + getInvokableStmt(mainMethodSignature, methodInterface))); + assertFalse( + cg.containsCall( + mainMethodSignature, + methodInterfaceNoImplementation, + getInvokableStmt(mainMethodSignature, methodInterface))); + assertTrue( + cg.containsCall( + mainMethodSignature, + methodInterfaceImplementationNotInstatiated, + getInvokableStmt(mainMethodSignature, methodInterface))); } @Test @@ -238,6 +331,9 @@ public void checkCallGraphDotExporter() { + "\t\"\" -> \"()>\";\n" + "\t\"\" -> \"\";\n" + "\t\"\" -> \"()>\";\n" + + "\t\"\" -> \"()>\";\n" + + "\t\"\" -> \"()>\";\n" + + "\t\"\" -> \"()>\";\n" + "\t\"\" -> \"()>\";\n" + "\t\"\" -> \"\";\n" + "\t\"\" -> \"()>\";\n"