From 4535a573f384d0a8003a645520ab70a7548a9cff Mon Sep 17 00:00:00 2001 From: Jonas Klauke Date: Mon, 30 Oct 2023 18:13:40 +0100 Subject: [PATCH 01/23] Changed the CHA resolving to safe time. Transformed Callgraphs tests to bytecode. Improved Default Method resolving --- .../ClassHierarchyAnalysisAlgorithm.java | 45 +++-- .../sootup/callgraph/CallGraphTestBase.java | 60 ++++-- .../{source => binary}/ConcreteCall.java | 0 .../ConcreteCallDefaultInterface.java | 2 +- .../ConcreteCallDefaultSubInterface.java | 0 ...creteCallSubClassDiffDefaultInterface.java | 30 +++ ...reteCallSuperClassDefaultSubInterface.java | 0 ...eteCallSuperClassWithDefaultInterface.java | 2 +- .../ConcreteCall/binary/cvc/Class.class | Bin 0 -> 349 bytes .../ConcreteCall/binary/cvci/Class.class | Bin 0 -> 349 bytes .../ConcreteCall/binary/cvci/Interface.class | Bin 0 -> 202 bytes .../ConcreteCall/binary/cvcscddi/Class.class | Bin 0 -> 409 bytes .../binary/cvcscddi/Interface.class | Bin 0 -> 218 bytes .../binary/cvcscddi/SubClass.class | Bin 0 -> 368 bytes .../binary/cvcscddi/SubInterface.class | Bin 0 -> 247 bytes .../ConcreteCall/binary/cvcscsi/Class.class | Bin 0 -> 370 bytes .../binary/cvcscsi/Interface.class | Bin 0 -> 218 bytes .../binary/cvcscsi/SubInterface.class | Bin 0 -> 246 bytes .../binary/cvcscsi/SuperClass.class | Bin 0 -> 261 bytes .../ConcreteCall/binary/cvcscwi/Class.class | Bin 0 -> 371 bytes .../binary/cvcscwi/Interface.class | Bin 0 -> 219 bytes .../binary/cvcscwi/SuperClass.class | Bin 0 -> 282 bytes .../ConcreteCall/binary/cvcsi/Class.class | Bin 0 -> 357 bytes .../ConcreteCall/binary/cvcsi/Interface.class | Bin 0 -> 206 bytes .../binary/cvcsi/SubInterface.class | Bin 0 -> 232 bytes .../InterfaceMethod/binary/J8DIM0.java | 19 ++ .../{source => binary}/J8DIM1.java | 1 + .../{source => binary}/J8DIM2.java | 0 .../{source => binary}/J8DIM3.java | 0 .../{source => binary}/J8DIM4.java | 0 .../{source => binary}/J8DIM5.java | 0 .../{source => binary}/J8SIM.java | 0 .../InterfaceMethod/binary/j8dim0/Class.class | Bin 0 -> 372 bytes .../binary/j8dim0/Interface.class | Bin 0 -> 126 bytes .../InterfaceMethod/binary/j8dim1/Class.class | Bin 0 -> 333 bytes .../binary/j8dim1/Interface.class | Bin 0 -> 182 bytes .../binary/j8dim2/Interface.class | Bin 0 -> 182 bytes .../binary/j8dim2/SubClass.class | Bin 0 -> 220 bytes .../binary/j8dim2/SuperClass.class | Bin 0 -> 396 bytes .../binary/j8dim3/Interface.class | Bin 0 -> 182 bytes .../binary/j8dim3/SubClass.class | Bin 0 -> 220 bytes .../binary/j8dim3/SuperClass.class | Bin 0 -> 372 bytes .../binary/j8dim4/Interface.class | Bin 0 -> 182 bytes .../binary/j8dim4/SubClass.class | Bin 0 -> 220 bytes .../binary/j8dim4/SuperClass.class | Bin 0 -> 333 bytes .../InterfaceMethod/binary/j8dim5/Class.class | Bin 0 -> 273 bytes .../binary/j8dim5/DirectInterface.class | Bin 0 -> 238 bytes .../binary/j8dim5/Interface1.class | Bin 0 -> 201 bytes .../binary/j8dim5/Interface2.class | Bin 0 -> 201 bytes .../binary/j8dim5/SuperClass.class | Bin 0 -> 397 bytes .../InterfaceMethod/binary/j8sim/Class.class | Bin 0 -> 308 bytes .../binary/j8sim/Interface.class | Bin 0 -> 180 bytes .../typehierarchy/MethodDispatchResolver.java | 180 ++++++++---------- .../MethodDispatchResolverTest.java | 30 ++- .../AbstractDispatchTest.java | 15 +- 55 files changed, 241 insertions(+), 143 deletions(-) rename sootup.callgraph/src/test/resources/callgraph/ConcreteCall/{source => binary}/ConcreteCall.java (100%) rename sootup.callgraph/src/test/resources/callgraph/ConcreteCall/{source => binary}/ConcreteCallDefaultInterface.java (84%) rename sootup.callgraph/src/test/resources/callgraph/ConcreteCall/{source => binary}/ConcreteCallDefaultSubInterface.java (100%) create mode 100644 sootup.callgraph/src/test/resources/callgraph/ConcreteCall/binary/ConcreteCallSubClassDiffDefaultInterface.java rename sootup.callgraph/src/test/resources/callgraph/ConcreteCall/{source => binary}/ConcreteCallSuperClassDefaultSubInterface.java (100%) rename sootup.callgraph/src/test/resources/callgraph/ConcreteCall/{source => binary}/ConcreteCallSuperClassWithDefaultInterface.java (92%) create mode 100644 sootup.callgraph/src/test/resources/callgraph/ConcreteCall/binary/cvc/Class.class create mode 100644 sootup.callgraph/src/test/resources/callgraph/ConcreteCall/binary/cvci/Class.class create mode 100644 sootup.callgraph/src/test/resources/callgraph/ConcreteCall/binary/cvci/Interface.class create mode 100644 sootup.callgraph/src/test/resources/callgraph/ConcreteCall/binary/cvcscddi/Class.class create mode 100644 sootup.callgraph/src/test/resources/callgraph/ConcreteCall/binary/cvcscddi/Interface.class create mode 100644 sootup.callgraph/src/test/resources/callgraph/ConcreteCall/binary/cvcscddi/SubClass.class create mode 100644 sootup.callgraph/src/test/resources/callgraph/ConcreteCall/binary/cvcscddi/SubInterface.class create mode 100644 sootup.callgraph/src/test/resources/callgraph/ConcreteCall/binary/cvcscsi/Class.class create mode 100644 sootup.callgraph/src/test/resources/callgraph/ConcreteCall/binary/cvcscsi/Interface.class create mode 100644 sootup.callgraph/src/test/resources/callgraph/ConcreteCall/binary/cvcscsi/SubInterface.class create mode 100644 sootup.callgraph/src/test/resources/callgraph/ConcreteCall/binary/cvcscsi/SuperClass.class create mode 100644 sootup.callgraph/src/test/resources/callgraph/ConcreteCall/binary/cvcscwi/Class.class create mode 100644 sootup.callgraph/src/test/resources/callgraph/ConcreteCall/binary/cvcscwi/Interface.class create mode 100644 sootup.callgraph/src/test/resources/callgraph/ConcreteCall/binary/cvcscwi/SuperClass.class create mode 100644 sootup.callgraph/src/test/resources/callgraph/ConcreteCall/binary/cvcsi/Class.class create mode 100644 sootup.callgraph/src/test/resources/callgraph/ConcreteCall/binary/cvcsi/Interface.class create mode 100644 sootup.callgraph/src/test/resources/callgraph/ConcreteCall/binary/cvcsi/SubInterface.class create mode 100644 sootup.callgraph/src/test/resources/callgraph/InterfaceMethod/binary/J8DIM0.java rename sootup.callgraph/src/test/resources/callgraph/InterfaceMethod/{source => binary}/J8DIM1.java (96%) rename sootup.callgraph/src/test/resources/callgraph/InterfaceMethod/{source => binary}/J8DIM2.java (100%) rename sootup.callgraph/src/test/resources/callgraph/InterfaceMethod/{source => binary}/J8DIM3.java (100%) rename sootup.callgraph/src/test/resources/callgraph/InterfaceMethod/{source => binary}/J8DIM4.java (100%) rename sootup.callgraph/src/test/resources/callgraph/InterfaceMethod/{source => binary}/J8DIM5.java (100%) rename sootup.callgraph/src/test/resources/callgraph/InterfaceMethod/{source => binary}/J8SIM.java (100%) create mode 100644 sootup.callgraph/src/test/resources/callgraph/InterfaceMethod/binary/j8dim0/Class.class create mode 100644 sootup.callgraph/src/test/resources/callgraph/InterfaceMethod/binary/j8dim0/Interface.class create mode 100644 sootup.callgraph/src/test/resources/callgraph/InterfaceMethod/binary/j8dim1/Class.class create mode 100644 sootup.callgraph/src/test/resources/callgraph/InterfaceMethod/binary/j8dim1/Interface.class create mode 100644 sootup.callgraph/src/test/resources/callgraph/InterfaceMethod/binary/j8dim2/Interface.class create mode 100644 sootup.callgraph/src/test/resources/callgraph/InterfaceMethod/binary/j8dim2/SubClass.class create mode 100644 sootup.callgraph/src/test/resources/callgraph/InterfaceMethod/binary/j8dim2/SuperClass.class create mode 100644 sootup.callgraph/src/test/resources/callgraph/InterfaceMethod/binary/j8dim3/Interface.class create mode 100644 sootup.callgraph/src/test/resources/callgraph/InterfaceMethod/binary/j8dim3/SubClass.class create mode 100644 sootup.callgraph/src/test/resources/callgraph/InterfaceMethod/binary/j8dim3/SuperClass.class create mode 100644 sootup.callgraph/src/test/resources/callgraph/InterfaceMethod/binary/j8dim4/Interface.class create mode 100644 sootup.callgraph/src/test/resources/callgraph/InterfaceMethod/binary/j8dim4/SubClass.class create mode 100644 sootup.callgraph/src/test/resources/callgraph/InterfaceMethod/binary/j8dim4/SuperClass.class create mode 100644 sootup.callgraph/src/test/resources/callgraph/InterfaceMethod/binary/j8dim5/Class.class create mode 100644 sootup.callgraph/src/test/resources/callgraph/InterfaceMethod/binary/j8dim5/DirectInterface.class create mode 100644 sootup.callgraph/src/test/resources/callgraph/InterfaceMethod/binary/j8dim5/Interface1.class create mode 100644 sootup.callgraph/src/test/resources/callgraph/InterfaceMethod/binary/j8dim5/Interface2.class create mode 100644 sootup.callgraph/src/test/resources/callgraph/InterfaceMethod/binary/j8dim5/SuperClass.class create mode 100644 sootup.callgraph/src/test/resources/callgraph/InterfaceMethod/binary/j8sim/Class.class create mode 100644 sootup.callgraph/src/test/resources/callgraph/InterfaceMethod/binary/j8sim/Interface.class diff --git a/sootup.callgraph/src/main/java/sootup/callgraph/ClassHierarchyAnalysisAlgorithm.java b/sootup.callgraph/src/main/java/sootup/callgraph/ClassHierarchyAnalysisAlgorithm.java index d0f22f5323c..2eb2516ef62 100644 --- a/sootup.callgraph/src/main/java/sootup/callgraph/ClassHierarchyAnalysisAlgorithm.java +++ b/sootup.callgraph/src/main/java/sootup/callgraph/ClassHierarchyAnalysisAlgorithm.java @@ -27,6 +27,7 @@ import javax.annotation.Nonnull; import sootup.core.jimple.common.expr.AbstractInvokeExpr; import sootup.core.jimple.common.expr.JDynamicInvokeExpr; +import sootup.core.jimple.common.expr.JInterfaceInvokeExpr; import sootup.core.jimple.common.expr.JSpecialInvokeExpr; import sootup.core.model.MethodModifier; import sootup.core.model.SootClass; @@ -81,33 +82,26 @@ protected Stream resolveCall(SootMethod method, AbstractInvokeE return Stream.empty(); } - Stream result = Stream.of(targetMethodSignature); - SootMethod targetMethod = - view.getClass(targetMethodSignature.getDeclClassType()) - .flatMap(clazz -> clazz.getMethod(targetMethodSignature.getSubSignature())) - .orElseGet(() -> findMethodInHierarchy(view, targetMethodSignature)); + MethodDispatchResolver.findConcreteMethod(view, targetMethodSignature).orElse(null); if (targetMethod == null || MethodModifier.isStatic(targetMethod.getModifiers()) || (invokeExpr instanceof JSpecialInvokeExpr)) { - return result; + return Stream.of(targetMethodSignature); } else { - if (targetMethod.isAbstract()) { - return resolveAllSubClassCallTargets(targetMethodSignature); + if (targetMethod.isAbstract() || invokeExpr instanceof JInterfaceInvokeExpr) { + // abstract method call or interface call + // TODO: SpeedUp by only resolving direct subclasses + return resolveAllCallTargets(targetMethodSignature); } - return MethodDispatchResolver.resolveConcreteDispatch(view, targetMethodSignature) - .map( - methodSignature -> - Stream.concat( - Stream.of(methodSignature), - resolveAllSubClassCallTargets(targetMethodSignature))) - .orElseGet(() -> resolveAllSubClassCallTargets(targetMethodSignature)); + return Stream.concat( + Stream.of(targetMethod.getSignature()), + resolveAllSubClassCallTargets(targetMethodSignature, targetMethod)); } } - private Stream resolveAllSubClassCallTargets( - MethodSignature targetMethodSignature) { + private Stream resolveAllCallTargets(MethodSignature targetMethodSignature) { return MethodDispatchResolver.resolveAllDispatches(view, targetMethodSignature).stream() .map( methodSignature -> @@ -116,6 +110,23 @@ private Stream resolveAllSubClassCallTargets( .map(Optional::get); } + private Stream resolveAllSubClassCallTargets( + MethodSignature targetMethodSignature, SootMethod targetMethod) { + SootClass sootClass = view.getClass(targetMethod.getDeclaringClassType()).orElse(null); + if (sootClass == null) { + return Stream.empty(); + } + if (sootClass.isInterface()) { + // the super call target is a default method of an Interface. + // If subtypes of class in the target signature does not implement the method, + // other default method which are subtypes of the Interface can be targets + } + + // the concrete target of the hierarchical highest class of call targets is known. + // this method can be only overwritten by implemented methods of the subtypes + return MethodDispatchResolver.resolveAbstractDispatch(view, targetMethodSignature); + } + @Override protected void postProcessingMethod( View> view, diff --git a/sootup.callgraph/src/test/java/sootup/callgraph/CallGraphTestBase.java b/sootup.callgraph/src/test/java/sootup/callgraph/CallGraphTestBase.java index cefcac8de44..647c9ba63c7 100644 --- a/sootup.callgraph/src/test/java/sootup/callgraph/CallGraphTestBase.java +++ b/sootup.callgraph/src/test/java/sootup/callgraph/CallGraphTestBase.java @@ -140,7 +140,7 @@ public void testRecursiveCall() { @Test public void testConcreteCall() { - CallGraph cg = loadCallGraph("ConcreteCall", "cvc.Class"); + CallGraph cg = loadCallGraph("ConcreteCall", false, "cvc.Class"); MethodSignature targetMethod = identifierFactory.getMethodSignature( identifierFactory.getClassType("cvc.Class"), "target", "void", Collections.emptyList()); @@ -159,9 +159,28 @@ public void testConcreteCallInSuperClass() { assertTrue(cg.containsCall(mainMethodSignature, targetMethod)); } + @Test + public void testConcreteCallDifferentDefaultMethodInSubClass() { + CallGraph cg = loadCallGraph("ConcreteCall", false, "cvcscddi.Class"); + MethodSignature interfaceMethod = + identifierFactory.getMethodSignature( + identifierFactory.getClassType("cvcscddi.Interface"), + "method", + "void", + Collections.emptyList()); + MethodSignature subInterfaceMethod = + identifierFactory.getMethodSignature( + identifierFactory.getClassType("cvcscddi.SubInterface"), + "method", + "void", + Collections.emptyList()); + assertTrue(cg.containsCall(mainMethodSignature, interfaceMethod)); + assertTrue(cg.containsCall(mainMethodSignature, subInterfaceMethod)); + } + @Test public void testConcreteCallInSuperClassWithDefaultInterface() { - CallGraph cg = loadCallGraph("ConcreteCall", "cvcscwi.Class"); + CallGraph cg = loadCallGraph("ConcreteCall", false, "cvcscwi.Class"); MethodSignature targetMethod = identifierFactory.getMethodSignature( identifierFactory.getClassType("cvcscwi.SuperClass"), @@ -173,7 +192,7 @@ public void testConcreteCallInSuperClassWithDefaultInterface() { @Test public void testConcreteCallInInterface() { - CallGraph cg = loadCallGraph("ConcreteCall", "cvci.Class"); + CallGraph cg = loadCallGraph("ConcreteCall", false, "cvci.Class"); MethodSignature targetMethod = identifierFactory.getMethodSignature( identifierFactory.getClassType("cvci.Interface"), @@ -185,7 +204,7 @@ public void testConcreteCallInInterface() { @Test public void testConcreteCallInSubInterface() { - CallGraph cg = loadCallGraph("ConcreteCall", "cvcsi.Class"); + CallGraph cg = loadCallGraph("ConcreteCall", false, "cvcsi.Class"); MethodSignature targetMethod = identifierFactory.getMethodSignature( identifierFactory.getClassType("cvcsi.SubInterface"), @@ -197,7 +216,7 @@ public void testConcreteCallInSubInterface() { @Test public void testConcreteCallInSuperClassSubInterface() { - CallGraph cg = loadCallGraph("ConcreteCall", "cvcscsi.Class"); + CallGraph cg = loadCallGraph("ConcreteCall", false, "cvcscsi.Class"); MethodSignature targetMethod = identifierFactory.getMethodSignature( identifierFactory.getClassType("cvcscsi.SubInterface"), @@ -442,9 +461,28 @@ public void testVirtualCall4() { assertTrue(cg.containsCall(mainMethodSignature, callMethod)); } + @Test + public void testDynamicInterfaceMethod0() { + CallGraph cg = loadCallGraph("InterfaceMethod", false, "j8dim0.Class"); + MethodSignature interfaceMethod = + identifierFactory.getMethodSignature( + identifierFactory.getClassType("j8dim0.Interface"), + "method", + "void", + Collections.emptyList()); + MethodSignature classMethod = + identifierFactory.getMethodSignature( + identifierFactory.getClassType("j8dim0.Class"), + "method", + "void", + Collections.emptyList()); + assertFalse(cg.containsCall(mainMethodSignature, interfaceMethod)); + assertTrue(cg.containsCall(mainMethodSignature, classMethod)); + } + @Test public void testDynamicInterfaceMethod1() { - CallGraph cg = loadCallGraph("InterfaceMethod", "j8dim1.Class"); + CallGraph cg = loadCallGraph("InterfaceMethod", false, "j8dim1.Class"); MethodSignature callMethod = identifierFactory.getMethodSignature( identifierFactory.getClassType("j8dim1.Interface"), @@ -456,7 +494,7 @@ public void testDynamicInterfaceMethod1() { @Test public void testDynamicInterfaceMethod2() { - CallGraph cg = loadCallGraph("InterfaceMethod", "j8dim2.SuperClass"); + CallGraph cg = loadCallGraph("InterfaceMethod", false, "j8dim2.SuperClass"); MethodSignature callMethod = identifierFactory.getMethodSignature( @@ -469,7 +507,7 @@ public void testDynamicInterfaceMethod2() { @Test public void testDynamicInterfaceMethod3() { - CallGraph cg = loadCallGraph("InterfaceMethod", "j8dim3.SuperClass"); + CallGraph cg = loadCallGraph("InterfaceMethod", false, "j8dim3.SuperClass"); MethodSignature callMethod = identifierFactory.getMethodSignature( @@ -479,7 +517,7 @@ public void testDynamicInterfaceMethod3() { @Test public void testDynamicInterfaceMethod4() { - CallGraph cg = loadCallGraph("InterfaceMethod", "j8dim4.SuperClass"); + CallGraph cg = loadCallGraph("InterfaceMethod", false, "j8dim4.SuperClass"); MethodSignature callMethod = identifierFactory.getMethodSignature( @@ -492,7 +530,7 @@ public void testDynamicInterfaceMethod4() { @Test public void testDynamicInterfaceMethod5() { - CallGraph cg = loadCallGraph("InterfaceMethod", "j8dim5.SuperClass"); + CallGraph cg = loadCallGraph("InterfaceMethod", false, "j8dim5.SuperClass"); MethodSignature method = identifierFactory.getMethodSignature( @@ -539,7 +577,7 @@ public void testDynamicInterfaceMethod6() { @Test public void testStaticInterfaceMethod() { - CallGraph cg = loadCallGraph("InterfaceMethod", "j8sim.Class"); + CallGraph cg = loadCallGraph("InterfaceMethod", false, "j8sim.Class"); MethodSignature method = identifierFactory.getMethodSignature( diff --git a/sootup.callgraph/src/test/resources/callgraph/ConcreteCall/source/ConcreteCall.java b/sootup.callgraph/src/test/resources/callgraph/ConcreteCall/binary/ConcreteCall.java similarity index 100% rename from sootup.callgraph/src/test/resources/callgraph/ConcreteCall/source/ConcreteCall.java rename to sootup.callgraph/src/test/resources/callgraph/ConcreteCall/binary/ConcreteCall.java diff --git a/sootup.callgraph/src/test/resources/callgraph/ConcreteCall/source/ConcreteCallDefaultInterface.java b/sootup.callgraph/src/test/resources/callgraph/ConcreteCall/binary/ConcreteCallDefaultInterface.java similarity index 84% rename from sootup.callgraph/src/test/resources/callgraph/ConcreteCall/source/ConcreteCallDefaultInterface.java rename to sootup.callgraph/src/test/resources/callgraph/ConcreteCall/binary/ConcreteCallDefaultInterface.java index 9db1421d553..2f4d832ab5b 100644 --- a/sootup.callgraph/src/test/resources/callgraph/ConcreteCall/source/ConcreteCallDefaultInterface.java +++ b/sootup.callgraph/src/test/resources/callgraph/ConcreteCall/binary/ConcreteCallDefaultInterface.java @@ -1,7 +1,7 @@ // cvci/Class.java package cvci; -class Class implements Interface{ +class Class implements SInterface{ public static void main(String[] args){ Class cls = new Class(); diff --git a/sootup.callgraph/src/test/resources/callgraph/ConcreteCall/source/ConcreteCallDefaultSubInterface.java b/sootup.callgraph/src/test/resources/callgraph/ConcreteCall/binary/ConcreteCallDefaultSubInterface.java similarity index 100% rename from sootup.callgraph/src/test/resources/callgraph/ConcreteCall/source/ConcreteCallDefaultSubInterface.java rename to sootup.callgraph/src/test/resources/callgraph/ConcreteCall/binary/ConcreteCallDefaultSubInterface.java diff --git a/sootup.callgraph/src/test/resources/callgraph/ConcreteCall/binary/ConcreteCallSubClassDiffDefaultInterface.java b/sootup.callgraph/src/test/resources/callgraph/ConcreteCall/binary/ConcreteCallSubClassDiffDefaultInterface.java new file mode 100644 index 00000000000..7e6c61695de --- /dev/null +++ b/sootup.callgraph/src/test/resources/callgraph/ConcreteCall/binary/ConcreteCallSubClassDiffDefaultInterface.java @@ -0,0 +1,30 @@ +package cvcscddi; + +class Class implements Interface{ + + public static void main(String[] args){ + Class cls = new Class(); + Class cls2 = new SubClass(); + cls.target(); + } +} + +class SubClass extends Class implements SubInterface{ + + public static void main(String[] args){ + Class cls = new Class(); + cls.target(); + } +} + +interface Interface { + + default void target(){ } + +} + +interface SubInterface extends Interface { + + default void target(){ } + +} diff --git a/sootup.callgraph/src/test/resources/callgraph/ConcreteCall/source/ConcreteCallSuperClassDefaultSubInterface.java b/sootup.callgraph/src/test/resources/callgraph/ConcreteCall/binary/ConcreteCallSuperClassDefaultSubInterface.java similarity index 100% rename from sootup.callgraph/src/test/resources/callgraph/ConcreteCall/source/ConcreteCallSuperClassDefaultSubInterface.java rename to sootup.callgraph/src/test/resources/callgraph/ConcreteCall/binary/ConcreteCallSuperClassDefaultSubInterface.java diff --git a/sootup.callgraph/src/test/resources/callgraph/ConcreteCall/source/ConcreteCallSuperClassWithDefaultInterface.java b/sootup.callgraph/src/test/resources/callgraph/ConcreteCall/binary/ConcreteCallSuperClassWithDefaultInterface.java similarity index 92% rename from sootup.callgraph/src/test/resources/callgraph/ConcreteCall/source/ConcreteCallSuperClassWithDefaultInterface.java rename to sootup.callgraph/src/test/resources/callgraph/ConcreteCall/binary/ConcreteCallSuperClassWithDefaultInterface.java index f16a278fdb8..b2b750b4937 100644 --- a/sootup.callgraph/src/test/resources/callgraph/ConcreteCall/source/ConcreteCallSuperClassWithDefaultInterface.java +++ b/sootup.callgraph/src/test/resources/callgraph/ConcreteCall/binary/ConcreteCallSuperClassWithDefaultInterface.java @@ -15,7 +15,7 @@ public void target(){ } } -class Interface { +interface Interface { default void target(){ } diff --git a/sootup.callgraph/src/test/resources/callgraph/ConcreteCall/binary/cvc/Class.class b/sootup.callgraph/src/test/resources/callgraph/ConcreteCall/binary/cvc/Class.class new file mode 100644 index 0000000000000000000000000000000000000000..413b6747ffd0e87b7db5342a67ee0b22c3bd0930 GIT binary patch literal 349 zcmZ9G%}T>S6ot>^*QROHDhMtFmsQbh+$nAXu7WNqg1DN*AxudVNT+=-Hx?9p03S*` z6NG|`x%bYQ@0_{cKObKJu5b{+M-pNZ!9ilnG{n9@aHAV-ZUx-g>63t$wXaGb$#tXd z*ULh6kFuz#1V(n1G9;E#Hv)&VmwX{NGOcA(rE}BirnW*6-K*%soB1JioI ziXLywW3}i}|4QON7SJ+8{D)oeCx__h6VCG%Zhrm_Z)X|z3k;YT(K4iIMA*Uj7bYDx A4gdfE literal 0 HcmV?d00001 diff --git a/sootup.callgraph/src/test/resources/callgraph/ConcreteCall/binary/cvci/Class.class b/sootup.callgraph/src/test/resources/callgraph/ConcreteCall/binary/cvci/Class.class new file mode 100644 index 0000000000000000000000000000000000000000..ebfa4e1a496a174ea8b98fc83f724108d2fc324d GIT binary patch literal 349 zcmYL^Jx{|h5QbmpqiI8$l+O(bwgQ4IY#^2(B%}z2A|%A*7&mg|I#TNNf3Z*rBz^!t z3UTg02j9JSexI}N=lAm)z$Nx$gvcU{VtB}0*p9FhVV9wIt&Fla4F34!nIR~eH_4Ed z%E*UqUdi@}SDHG(oGZg{Fn%p(e93doO`T6{t4w|6z7xKdK2_p1~5=)`-Li5EU!Jg}kHq#%gnG}{GU7P-S`m^P2EiH~{kq}hV zk{%rzN!5VVA!SB6JY9jklc1+4_SVgSEL%uOJL@^24jz8Nds)FR&pr@rkQ6>$y2*V) L1_TYM_K^MonrK0T literal 0 HcmV?d00001 diff --git a/sootup.callgraph/src/test/resources/callgraph/ConcreteCall/binary/cvci/Interface.class b/sootup.callgraph/src/test/resources/callgraph/ConcreteCall/binary/cvci/Interface.class new file mode 100644 index 0000000000000000000000000000000000000000..5e818d132036f7e186129225751bad317eb9545a GIT binary patch literal 202 zcmX|*u?~Vj5Jcw`K`}vFOFM0(@eeR2CPoX5{cDFSK6}U zgpAp7MvZZY%CXHz^QNSh6e8W~BK}i_dBzg@MfJTn4}s9Z6dBMCh|W P0}#Em14R literal 0 HcmV?d00001 diff --git a/sootup.callgraph/src/test/resources/callgraph/ConcreteCall/binary/cvcscddi/Class.class b/sootup.callgraph/src/test/resources/callgraph/ConcreteCall/binary/cvcscddi/Class.class new file mode 100644 index 0000000000000000000000000000000000000000..3181ff804c85251b20a8c5afacc9fd6030242d87 GIT binary patch literal 409 zcmYLF!AiqG5Ph4ZO^vlCv8`SNk5$oLyoll@MG&GMDuQ_0Y&T^~x`lMp|MFtNf*;^V ziL(XU!_J#|ym_-TKfhn!0IqNpz(?dG4&Yck2;d>IMcYTmN0*^~tF$tA3~ulAmBCA9 zBgwFzDlMO?X(r1To)yG+Q?4{axA&e-_=5KfuE+hMDU}}I*v4Qut4bm7l@(tkGc8JK zWWtMLSY=7U=ktNe^MTBHRhS2DWSMgzFRfP-I|vwBVj<>YG*bPwoFUo}{}xRgT4lKX z!E-i|!Z5@e?Z%sSGrSy2gA-cV2SbC7;?SdURP9oBOvngpXG^dTN*J0H>ub^_;T~F) rTj@2mx`dq-oVO+1bOn#F_IUn@`nENu3tNK*O$;fv$%O7noP+QWl9f`} literal 0 HcmV?d00001 diff --git a/sootup.callgraph/src/test/resources/callgraph/ConcreteCall/binary/cvcscddi/Interface.class b/sootup.callgraph/src/test/resources/callgraph/ConcreteCall/binary/cvcscddi/Interface.class new file mode 100644 index 0000000000000000000000000000000000000000..0f041d01fcb80f94d81e933207b31d55a772c006 GIT binary patch literal 218 zcmX|*y$*sv5QOIx!Jk57Vd5KTp)|e$#>B*Ep|M|%g+NY7j>Gq|weSIaDC1&bGqW?_ z7W4i*UjQ~31sDVvD)^3U#!jKTSY8#ps7hF2e8@{aHn*7Vg<>Pk@LV-k^Db}0Dym9t z*>Ob1oSQf@RM*=)O}CsS7@SSgMq`>Z{@nzRRjIjnQ@o@ILhuq1#M36(gq3s)$)@k+rGkPF;6sVu z7OftZnQwMxU_QUzJ^)-`--Cs~MQCvBB66|IFuamV>T3pja`M37B#TlogsD{GuAk?k zz2|vDiZka@F&s{w(r3Qpal=&|XS$WDzBEBETl8%qZlp0!lZ7f;p+&-*ChPO0;axYA zRW%b8?;Cxqv}h|{h;!5BV+fZaD3(Q6l%1DV8~^|S literal 0 HcmV?d00001 diff --git a/sootup.callgraph/src/test/resources/callgraph/ConcreteCall/binary/cvcscddi/SubInterface.class b/sootup.callgraph/src/test/resources/callgraph/ConcreteCall/binary/cvcscddi/SubInterface.class new file mode 100644 index 0000000000000000000000000000000000000000..e308473e8c4b293aabf17c97752b24bc538457b7 GIT binary patch literal 247 zcmX|*Jqp4=5QX3RllY4wSO^|K3r*t#ZxP!QMMg;Hn3q=bCjA|Y zFweAQ#~~SWEThm+QLNJ>S#v_AaofzXO-S?dN_SEi>$@&=9H)VVD^p?kvjaofB{)P^ p*7C-vidH!fP^d}Q<$ER7KvPsOCVwLI@n3vVPb^?6+LBpA`wKcsI)4BF literal 0 HcmV?d00001 diff --git a/sootup.callgraph/src/test/resources/callgraph/ConcreteCall/binary/cvcscsi/Class.class b/sootup.callgraph/src/test/resources/callgraph/ConcreteCall/binary/cvcscsi/Class.class new file mode 100644 index 0000000000000000000000000000000000000000..430e73707e9da3329d763b8ebd53312b2f74ab63 GIT binary patch literal 370 zcmYLEO-sW-6r9aR)5fH+_2VXZtcuOWo8l!R2*FE45Ko)94J+wZHka-S>%GQ~7I82$n2^GbpM literal 0 HcmV?d00001 diff --git a/sootup.callgraph/src/test/resources/callgraph/ConcreteCall/binary/cvcscsi/Interface.class b/sootup.callgraph/src/test/resources/callgraph/ConcreteCall/binary/cvcscsi/Interface.class new file mode 100644 index 0000000000000000000000000000000000000000..587d1e68aac88a758d4f0487cca18275bfae54ad GIT binary patch literal 218 zcmX|*I}XAy5Jbm(58pIKqDD=XgnYsU6SnUQ9c*4}E~lx>KTy3&>% zCuB_OZ)_7oO|#~lym6_|wv}U>lji6`cT$+?yKZz-#($tNZBcBfD&j+SVeJAyp(FEJ bmqF->_9e}KjkJ&7A%6%20W;B1yo13P(ws6; literal 0 HcmV?d00001 diff --git a/sootup.callgraph/src/test/resources/callgraph/ConcreteCall/binary/cvcscsi/SubInterface.class b/sootup.callgraph/src/test/resources/callgraph/ConcreteCall/binary/cvcscsi/SubInterface.class new file mode 100644 index 0000000000000000000000000000000000000000..781af7f185b24e5ea886b3f7ffc5bfb02e1cb0be GIT binary patch literal 246 zcmYk0&x*o84936lPphtr3hu!-(2KqJ1{GmJ^w4GR>1-@5V=L1c-)pZPd;lLxjDjK| zUy|P!LN3?S8NdRcA$lP`Li7~^M>b=p(45YG6B8Q?@^p8X>);@>cUI zzYp`cDz#T3dZC?g#JI8P&5z=_!8|%ZJ>Jt5=lB^ literal 0 HcmV?d00001 diff --git a/sootup.callgraph/src/test/resources/callgraph/ConcreteCall/binary/cvcscsi/SuperClass.class b/sootup.callgraph/src/test/resources/callgraph/ConcreteCall/binary/cvcscsi/SuperClass.class new file mode 100644 index 0000000000000000000000000000000000000000..61e6a34952a49d34b9b172834d88ae55802cdcfb GIT binary patch literal 261 zcmYLEu};H441I2CL(@PJVnyl*9h#*hVhO5Lse&O@_sij;tFD*oa@YT2LSo|Xe+%)UUM=k}#w+Xgbhk#G;` EKNN^Nx&QzG literal 0 HcmV?d00001 diff --git a/sootup.callgraph/src/test/resources/callgraph/ConcreteCall/binary/cvcscwi/Class.class b/sootup.callgraph/src/test/resources/callgraph/ConcreteCall/binary/cvcscwi/Class.class new file mode 100644 index 0000000000000000000000000000000000000000..56a3212b2e7d75f82862b8eb4dcdb191605386e4 GIT binary patch literal 371 zcmYLEO-sW-6r9aR)5fH+_2VXZtcvF1P4N;Dgy1Ejh^KAb#+7s{n@#;)UMwj11N>3q z+d}PO-y@Uq#HU`TSQ z#7#RL3w_JSH8I|lOU1A|ddy$=oToKcRazJ=Rdr^KVKHlUDXydyr`b%ES{RY>x-QyR zp|hGd&Al|wmtw-(+FUCm^n{n<#HK|E&}E3rdD)b2GF_@8Y+G`;BG)+#gBAIoLzft? zD`But=lP(SPIu^0G)+B1d(;_q|8N2JPJq^?+F6nwNj4A>w$fv=T66pb=V1XiKl*^T Tw)ElBr%mpXrAMZiW(WO0d{|2X literal 0 HcmV?d00001 diff --git a/sootup.callgraph/src/test/resources/callgraph/ConcreteCall/binary/cvcscwi/Interface.class b/sootup.callgraph/src/test/resources/callgraph/ConcreteCall/binary/cvcscwi/Interface.class new file mode 100644 index 0000000000000000000000000000000000000000..0374a362f67764eeb167f6b47edb45c20e99f51a GIT binary patch literal 219 zcmX|*%?`m(6olvcX{iMg8*jiyH{PHkkq8UI`t&+zbG34N%X8UUcmNM2PAzO^&dhff z^Zq}GRb&VUShUeij6eECNHh#c~XTi$}?@* zaYV*!$}8K*P*Lm?cb@T)O5+xpV|ygclay|x(APIz=z9|WfkLlBp`q*)uH%%o^8kgK d?5kWBp&{CoH2gKvK7NP%!4m`wM1AoZT3;tkGRpt} literal 0 HcmV?d00001 diff --git a/sootup.callgraph/src/test/resources/callgraph/ConcreteCall/binary/cvcscwi/SuperClass.class b/sootup.callgraph/src/test/resources/callgraph/ConcreteCall/binary/cvcscwi/SuperClass.class new file mode 100644 index 0000000000000000000000000000000000000000..2e2733e8a4723a85be420dfa3050cc08f43378d6 GIT binary patch literal 282 zcmZ9GKT88a5XIkIk~=-m80;(rOSSMCOR+f#2x5we)^qD5ZdmtZZ!dl>E5X7K;D-|D zQYbhue|YaV@V>r3J^`F#oMV8U3`K@rLV97njhBSM@yR`5ST&DA*t@Y_ZoB7N!X4L6 zl{9i#N+jg-rVB=EPWI(-Md=|RYJrW>))x%G=4 QY+NP&V`Ig()l8880K1AeumAu6 literal 0 HcmV?d00001 diff --git a/sootup.callgraph/src/test/resources/callgraph/ConcreteCall/binary/cvcsi/Class.class b/sootup.callgraph/src/test/resources/callgraph/ConcreteCall/binary/cvcsi/Class.class new file mode 100644 index 0000000000000000000000000000000000000000..b0f3d81923953bf0b9e757f5711805ae391b7320 GIT binary patch literal 357 zcmYjMO-sW-5Ph4ErcKkP){mRu!9!Iv7jKG}h#&+#LV~DX*$) zIp+rO#fK_O=x$+n^GDa z&_O<^rehs?QQw~>tD Q(x-LqlVm`m5!DXHKSPB?#Q*>R literal 0 HcmV?d00001 diff --git a/sootup.callgraph/src/test/resources/callgraph/ConcreteCall/binary/cvcsi/Interface.class b/sootup.callgraph/src/test/resources/callgraph/ConcreteCall/binary/cvcsi/Interface.class new file mode 100644 index 0000000000000000000000000000000000000000..5b43aee51dad9fdd2a56e90085baca578713359d GIT binary patch literal 206 zcmX|*u?~Vj5Jcw?K`~KVdkbx(@eeR2CPoX4{oT0`$O*}L@L#qTet;ikTqta2cHV3@ z`}w}!0JfNf7>9@i0w--wPGGQFUj_W6zLCJ}SX6Xs?uzV1D#Ij7>&9x@7hPB;b)_vi zN~AG6%4B0)+NeY2$YxU0rj)JZE4tN>BK~86c~`_nR(X6@C21D`0v^BAIX?lPb;xPa T6Q<98;5`If0aMnH-NWbyOHM6k literal 0 HcmV?d00001 diff --git a/sootup.callgraph/src/test/resources/callgraph/ConcreteCall/binary/cvcsi/SubInterface.class b/sootup.callgraph/src/test/resources/callgraph/ConcreteCall/binary/cvcsi/SubInterface.class new file mode 100644 index 0000000000000000000000000000000000000000..19f701e592a08b53923de685dc9e7e6a594ad45c GIT binary patch literal 232 zcmYk0!4APd5QhJyT8a>Had+UN7jICJNQ6VheY-}}X0^%M_PtykJb;H1QxcKQ{4@Lg zGnsk5?hgP<^gZ|O>^3nmzc~!#+r#A6{S(MPV3J! zEOKSYQYf`v(OH(-MrFLoEt#`aw8&&71xBh;*EINd1ctR2XqhL$KF&z->cO|~H|_!g g4UQ(CEBHBRu`M!Q4z$|BpK_3WCCgGf4M3u_yK;D zcqa-48SZ1wJ@?F+ukZIy07LA!un;=faA6?yU}Mw4mV<2u`%Eg?oHLmH!+VC#xXJ`W z5KAR)7G)ypJDwDT*rjNmtBe>Wmx^Jx{}|`|g+~QfvuN7XQq4}af;+7ibt*2U-tw+S z7nAG3iDvq6u*Tr$qfC~AXk765+=r*JaHVvc?2B|1_7zKDvLjh~6@Y8hpTbSi+2t-_T)L SE%jOpKIwMRqr)gbE5Q#RbUEk% literal 0 HcmV?d00001 diff --git a/sootup.callgraph/src/test/resources/callgraph/InterfaceMethod/binary/j8dim0/Interface.class b/sootup.callgraph/src/test/resources/callgraph/InterfaceMethod/binary/j8dim0/Interface.class new file mode 100644 index 0000000000000000000000000000000000000000..acf45c29d50a9e704a2e91ad8870bee94727f5e0 GIT binary patch literal 126 zcmX^0Z`VEs1_l!bPId-%b_Nbc2DaSPl8pQmMh0dL%`ip=uHgLAqU2P!%$!t425v74 z7f)XUy{yEtL`DXIEQ^%PTmya2ypq(Sw8Uhf0s)YWeokUuy1su>R%&ty8v_F)0~62& TAk6|alYs@qVqgW5Obl!QnoAlz literal 0 HcmV?d00001 diff --git a/sootup.callgraph/src/test/resources/callgraph/InterfaceMethod/binary/j8dim1/Class.class b/sootup.callgraph/src/test/resources/callgraph/InterfaceMethod/binary/j8dim1/Class.class new file mode 100644 index 0000000000000000000000000000000000000000..0815d28b26acf09af631cacb64bd7022c071feed GIT binary patch literal 333 zcmYLE%}T>S7@W<|Mw6J--<#mEDi{hq6cjHNp%V2_5yaCbS?h*m18M4ec`7RS06vuX zZ9Vj``_0VG4B!6zetrWOV&8|0*h9~Ug*X7mwuc=LyA1q7Y1Lga*!|;224~z9k|9o& zmUoLflkJ1ZDpH(UD9v!te@;vBCX!0%Su*WfrDx|R=uewPo6Bou%)!m*YH~X`GtLkm zHW6VPj1wYQpgstfwXH^z;J`!#Whfre~kJ14OIX7I6#F EAHnoE-2eap literal 0 HcmV?d00001 diff --git a/sootup.callgraph/src/test/resources/callgraph/InterfaceMethod/binary/j8dim1/Interface.class b/sootup.callgraph/src/test/resources/callgraph/InterfaceMethod/binary/j8dim1/Interface.class new file mode 100644 index 0000000000000000000000000000000000000000..f76e234bb54523e56424b0ee599c7dcb183a8cf3 GIT binary patch literal 182 zcmW+vI}UH F=nsKwBA@^O literal 0 HcmV?d00001 diff --git a/sootup.callgraph/src/test/resources/callgraph/InterfaceMethod/binary/j8dim2/Interface.class b/sootup.callgraph/src/test/resources/callgraph/InterfaceMethod/binary/j8dim2/Interface.class new file mode 100644 index 0000000000000000000000000000000000000000..34672fc650297f9e82def4decde54bc2d222fe4e GIT binary patch literal 182 zcmW+vI}U0xl=KFhp z0Ic9ij3rzFry~7mGJ(N-aTT!FO-2HfeO}YCs}gE2Dk(W}&rR2+w9Sq2cF`t2gv&xb zm4IJFSzd)fTx)7?D&>K1nxItmJvb!=rP=`mEWS3(XRsNE>}Gw!>8lsKgToatWt7Yo FMt_1^BBlTU literal 0 HcmV?d00001 diff --git a/sootup.callgraph/src/test/resources/callgraph/InterfaceMethod/binary/j8dim2/SubClass.class b/sootup.callgraph/src/test/resources/callgraph/InterfaceMethod/binary/j8dim2/SubClass.class new file mode 100644 index 0000000000000000000000000000000000000000..029fbd7eba0d7c3ce1175d1fed1ac6c33fa23b6e GIT binary patch literal 220 zcmX^0Z`VEs1_l!bel7-P25xo+9(D#^b_PC11~!|_yv!0iMh0dL%`ip=7U%qwR7M7V zpUk{eztY^K)S{5Yq#U3KS8#r5QF5wVCWyoBW#QuKYowQzSeD4cz{Xjeq1V3;68_Bnl;<-)tV)^Bj#URMEb4M9&ZVld zFGgpR%i%zg9hg|5*2yTA+0YN`Xq@u-+(fSfTPfW?gaT73K&)?+9l7(w+|LzFBH&rWQ``NER7|r(49sl;y-4Q+`-x}=F|Ua ggu3t0?iSF4!&kHzmPJmpfIPj>TzN~pc4q~ydsH(i_3HaEuGMVt5#E(`Tk z0)7!?c@+k6t*O1Kln1_Pf>PD@;FJ`UY6lRo_}Va^!Dbw?oAwE(uU_yD4p+d8Q8HT? F{Q-kkBCP-b literal 0 HcmV?d00001 diff --git a/sootup.callgraph/src/test/resources/callgraph/InterfaceMethod/binary/j8dim3/SubClass.class b/sootup.callgraph/src/test/resources/callgraph/InterfaceMethod/binary/j8dim3/SubClass.class new file mode 100644 index 0000000000000000000000000000000000000000..65da4ba8f0dd83aab3a4ab136846d591266011c2 GIT binary patch literal 220 zcmX^0Z`VEs1_l!bel7-P25xo+9(D#^b_PC11~!|_yv!0iMh0dL%`ip=7U%qwR7M7V zpUk{eztY^K)S{5Yq#U3KS8#r5QF5wVCWyoBW#QuKYpj=*SeD4cz{lgO?)pe78HB{ zA4;4JV!^}C4l}>+o7w&Pe*Xk;jG+w<*Fw*R0oO(oTNe5j1`PaEWvV=5Fh~3M42@YH zNrtYkGI>*_p)Br1m{5hMvV6)TB2u9;hV9X#ABz{^B_dmRL0PD5aiSgUAg_wKyii)z zx|*I}UQfoFbYNkFtmA2<(uo&T;Vco$rGu6(_x{SyvZxyj?s~v&7|VHyJzAa9lK_(r z`GH}RvR%3v-OYm)*efNp2Sr{Nb)*>-HM&!GiTID%)OOJR#g6Ew3_{xD*wng9R* literal 0 HcmV?d00001 diff --git a/sootup.callgraph/src/test/resources/callgraph/InterfaceMethod/binary/j8dim4/Interface.class b/sootup.callgraph/src/test/resources/callgraph/InterfaceMethod/binary/j8dim4/Interface.class new file mode 100644 index 0000000000000000000000000000000000000000..21e7659b2cd0920bf80a6d25246e3e61f18f4941 GIT binary patch literal 182 zcmW+vI}Utek~2$K)T6tB{vVc# BI(Gm7 literal 0 HcmV?d00001 diff --git a/sootup.callgraph/src/test/resources/callgraph/InterfaceMethod/binary/j8dim5/Class.class b/sootup.callgraph/src/test/resources/callgraph/InterfaceMethod/binary/j8dim5/Class.class new file mode 100644 index 0000000000000000000000000000000000000000..8625dc21d5bfc841144d841f960fded9c2e6228e GIT binary patch literal 273 zcmZvXzYYOG5XQfC+;YzUL?@Ie2q963hDZn+qF-kvD;!yO-b*D>cmNM2h8&e*zM1bg zlbOupdAkE>A!orrf+NY1;z)C3II;|8SGmgTF&O2_k|EqbtR+KgqFg!i_baIvVznb; z%@4kI+&@YkuzbJ(dOuIbQ!crX4k}LsE#KmhT1amB|~qcV!6vBSEfhdhD2J2Bu_oL zQiajimhEiKX&`QbVHjAxicG^+sq``@&SZKP9@Y9qZiFJf7<)IMjA8KY{#vs?Y_0(e g6`Cq)^faTXLnGznMkOJ7FOLRk5djuylXeBI4`E3$o&W#< literal 0 HcmV?d00001 diff --git a/sootup.callgraph/src/test/resources/callgraph/InterfaceMethod/binary/j8dim5/Interface1.class b/sootup.callgraph/src/test/resources/callgraph/InterfaceMethod/binary/j8dim5/Interface1.class new file mode 100644 index 0000000000000000000000000000000000000000..c16a7ba9cee235ccef1635891c78d769bb1ba1d5 GIT binary patch literal 201 zcmW+wI}UH z09ata;c|EkT$cJ#8^zF>`ez15D|0JT276hiln%F9q4u>-RCN-`of7w0)|FK2tTEmu zTE)9?mW!ug80S%%=`e^3qpFLLYR)ifs^Bh)Yj8+%C5;0xSo8w0Y?_5lilIwP^Q0%O Sr|i`VPUuht_=G)T3;jR*#wH8^ literal 0 HcmV?d00001 diff --git a/sootup.callgraph/src/test/resources/callgraph/InterfaceMethod/binary/j8dim5/Interface2.class b/sootup.callgraph/src/test/resources/callgraph/InterfaceMethod/binary/j8dim5/Interface2.class new file mode 100644 index 0000000000000000000000000000000000000000..ec52fef02b4f9a64dea4bb2a250120db1284ca9c GIT binary patch literal 201 zcmW+wI}UH z09ata;c|EkT$cJ#8^zF>&dv;uR_0cw4EC~2DIIRJLhWmvsOlt=J08lws`VQ@Rt0-8P*A*71f}YsB8aE7UD^#zQqsi#<;8-6KfoU) z&W1w4g`M|!Gw;3G{rUa;25^oY2NpaFbq5AK4I3IZEo@n62vjartgxGCPY3cw z>9*Co_Vw+(f2Nf#R$vRbVSA_|ztx!pvzd!+*e+a6cnhMJPi0!L0=4Ce$9^aW868J5 z8z)2a2o*j}pAs)egS(j>G784>$sFRH30)8?MbRR18Bt4j3zmxiGMkb$tS@9fKg^)i i`-1T@N2z=I0h719Y^%ehV2?KTIsX8)!bX)f1GPUkE4mPP`TyJ|AvLJs(F<;hXs|gZ}kd8bolq~c-)){LdfWXZ*t7l lC+LF#;ee-;-8|$HjGcAJ?O6W{-rgy8fr8^BRwLFu#D5v#HLCys literal 0 HcmV?d00001 diff --git a/sootup.callgraph/src/test/resources/callgraph/InterfaceMethod/binary/j8sim/Interface.class b/sootup.callgraph/src/test/resources/callgraph/InterfaceMethod/binary/j8sim/Interface.class new file mode 100644 index 0000000000000000000000000000000000000000..49a8ad82098c06a1b8a0f9e9e60b08ed3121eedb GIT binary patch literal 180 zcmX^0Z`VEs1_l!bZgvJvb_Om+2DaSPl8pQmMh0dL%`ip=7U%qwR7M7VpUk{eztY^K z)S{5Yq#U3KS8#r5QF5wVCYa-85$x%!mz7wS2vnP8QJk5p@0nMUT9lTU3{)!slF-jd z%uCnzPs&P7E@5L}U}RtdS`4B&8JHPZfHW(RCkYk resolveAllDispatches( TypeHierarchy hierarchy = view.getTypeHierarchy(); return hierarchy.subtypesOf(m.getDeclClassType()).stream() - .map( - subtype -> - view.getClass(subtype) - .orElseThrow( - () -> - new ResolveException( - "Could not resolve " + subtype + ", but found it in hierarchy."))) .filter( - sootClass -> { - SootMethod sootMethod = sootClass.getMethod(m.getSubSignature()).orElse(null); - // method is not implemented or not abstract + classType -> { + SootMethod sootMethod = + view.getMethod( + view.getIdentifierFactory() + .getMethodSignature(classType, m.getSubSignature())) + .orElse(null); + // methods are kept that are not implemented or not abstract return sootMethod == null || !sootMethod.isAbstract(); }) - .map(sootClass -> new MethodSignature(sootClass.getType(), m.getSubSignature())) + .map( + classType -> + view.getIdentifierFactory().getMethodSignature(classType, m.getSubSignature())) .collect(Collectors.toSet()); } @@ -74,24 +78,19 @@ public static Set resolveAllDispatches( * the set of method signatures that a method call could resolve to. */ @Nonnull - public static Set resolveAbstractDispatch( + public static Stream resolveAbstractDispatch( View> view, MethodSignature m) { TypeHierarchy hierarchy = view.getTypeHierarchy(); return hierarchy.subtypesOf(m.getDeclClassType()).stream() .map( - subtype -> - view.getClass(subtype) - .orElseThrow( - () -> - new ResolveException( - "Could not resolve " + subtype + ", but found it in hierarchy."))) - .map(sootClass -> sootClass.getMethod(m.getSubSignature())) + sootClass -> + view.getMethod( + view.getIdentifierFactory().getMethodSignature(sootClass, m.getSubSignature()))) .filter(Optional::isPresent) .map(Optional::get) .filter(method -> !method.isAbstract()) - .map(Method::getSignature) - .collect(Collectors.toSet()); + .map(Method::getSignature); } /** @@ -173,20 +172,6 @@ public static boolean canDispatch( || hierarchy.isSubtype(called.getType(), potentialTarget.getType())); // covariant } - /** - * Returns all superclasses of classType(inclusive) up to java.lang.Object - * , which will be the last entry in the list, or till one of the superclasses is not - * contained in view. - */ - private static List> findSuperClassesInclusive( - View> view, ClassType classType) { - return Stream.concat( - Stream.of(classType), - view.getTypeHierarchy().incompleteSuperClassesOf(classType).stream()) - .flatMap(t -> view.getClass(t).map(Stream::of).orElseGet(Stream::empty)) - .collect(Collectors.toList()); - } - /** * Searches for the signature of the method that is the concrete implementation of m. * This is done by checking each superclass and the class itself for whether it contains the @@ -195,74 +180,75 @@ private static List> findSuperClassesInclusive( @Nonnull public static Optional resolveConcreteDispatch( View> view, MethodSignature m) { - TypeHierarchy hierarchy = view.getTypeHierarchy(); - ClassType current = m.getDeclClassType(); - SootClass startClass = view.getClass(current).orElse(null); - List> classesInHierarchyOrder = findSuperClassesInclusive(view, current); - - for (SootClass currentClass : classesInHierarchyOrder) { - SootMethod method = currentClass.getMethod(m.getSubSignature()).orElse(null); - if (method != null) { - if (!method.isAbstract()) { - // found method is not abstract - return Optional.of(method.getSignature()); - } else { - if (startClass.isAbstract() - && !startClass.getType().equals(method.getDeclaringClassType())) { - // A not implemented method of an abstract class results into an abstract method - return Optional.empty(); - } - // found method is abstract and the startClass is not abstract - throw new ResolveException( - "Could not find concrete method for " + m + " because the method is abstract"); - } + Optional methodOp = findConcreteMethod(view, m); + if (methodOp.isPresent()) { + SootMethod method = methodOp.get(); + if (method.isAbstract()) { + return Optional.empty(); } + return Optional.of(method.getSignature()); } + return Optional.empty(); + } - // No super class contains the implemented method, search the concrete method in interfaces - // first collect all interfaces and super interfaces - List> worklist = - classesInHierarchyOrder.stream() - .flatMap(sootClass -> getSootClassesOfInterfaces(view, sootClass).stream()) - .collect(Collectors.toList()); - ArrayList> processedInterface = new ArrayList<>(); - ArrayList possibleDefaultMethods = new ArrayList<>(); - while (!worklist.isEmpty()) { - SootClass currentInterface = worklist.remove(0); - if (processedInterface.contains(currentInterface)) { - // interface was already processed - continue; - } - - // add found default method to possibleDefaultMethods - Optional concreteMethod = - currentInterface.getMethod(m.getSubSignature()); - concreteMethod.ifPresent(possibleDefaultMethods::add); + /** + * searches the method object in the given hierarchy + * + * @param view it contains all classes + * @param sig the signature of the searched method + * @return the found method object, or null if the method was not found. + */ + public static Optional findConcreteMethod( + @Nonnull View> view, @Nonnull MethodSignature sig) { + IdentifierFactory identifierFactory = view.getIdentifierFactory(); + Optional startMethod = view.getMethod(sig); + if (startMethod.isPresent()) { + return startMethod; + } + TypeHierarchy typeHierarchy = view.getTypeHierarchy(); - // if no default message is found search the default message in super interfaces - if (!concreteMethod.isPresent()) { - worklist.addAll(getSootClassesOfInterfaces(view, currentInterface)); + List superClasses = typeHierarchy.incompleteSuperClassesOf(sig.getDeclClassType()); + for (ClassType superClassType : superClasses) { + Optional method = + view.getMethod( + identifierFactory.getMethodSignature(superClassType, sig.getSubSignature())); + if (method.isPresent()) { + return method; } - processedInterface.add(currentInterface); } - - if (!possibleDefaultMethods.isEmpty()) { - // the interfaces are sorted by hierarchy - possibleDefaultMethods.sort( - (interface1, interface2) -> { - // interface1 is a sub-interface of interface2 - if (hierarchy.isSubtype( - interface2.getDeclaringClassType(), interface1.getDeclaringClassType())) return -1; - // interface1 is a super-interface of interface2 - if (hierarchy.isSubtype( - interface1.getDeclaringClassType(), interface2.getDeclaringClassType())) return 1; - // due to multiple inheritance in interfaces - return 0; - }); - // return the lowest element in the hierarchy - return Optional.of(possibleDefaultMethods.get(0).getSignature()); + Set interfaces = typeHierarchy.implementedInterfacesOf(sig.getDeclClassType()); + Optional defaultMethod = + interfaces.stream() + .map( + classType -> + view.getMethod( + identifierFactory.getMethodSignature(classType, sig.getSubSignature()))) + .filter(Optional::isPresent) + .map(Optional::get) + .sorted( + (interface1, interface2) -> { + // interface1 is a sub-interface of interface2 + if (typeHierarchy.isSubtype( + interface2.getDeclaringClassType(), interface1.getDeclaringClassType())) + return -1; + // interface1 is a super-interface of interface2 + if (typeHierarchy.isSubtype( + interface1.getDeclaringClassType(), interface2.getDeclaringClassType())) + return 1; + // due to multiple inheritance in interfaces + return 0; + }) + .findFirst(); + if (defaultMethod.isPresent()) { + return defaultMethod; } - throw new ResolveException("Could not find concrete method for " + m); + logger.warn( + "Could not find \"" + + sig.getSubSignature() + + "\" in " + + sig.getDeclClassType().getClassName() + + " and in its superclasses and interfaces"); + return Optional.empty(); } /** diff --git a/sootup.tests/src/test/java/sootup/tests/typehierarchy/MethodDispatchResolverTest.java b/sootup.tests/src/test/java/sootup/tests/typehierarchy/MethodDispatchResolverTest.java index 1e6fb17efee..4c7737c13e6 100644 --- a/sootup.tests/src/test/java/sootup/tests/typehierarchy/MethodDispatchResolverTest.java +++ b/sootup.tests/src/test/java/sootup/tests/typehierarchy/MethodDispatchResolverTest.java @@ -17,7 +17,6 @@ import org.junit.Test; import org.junit.experimental.categories.Category; import sootup.core.IdentifierFactory; -import sootup.core.frontend.ResolveException; import sootup.core.jimple.basic.Local; import sootup.core.jimple.common.expr.JSpecialInvokeExpr; import sootup.core.signatures.MethodSignature; @@ -73,20 +72,26 @@ public void resolveAbstractDispatch() { factory.parseMethodSignature("java.util.ArrayDeque#clone(): java.util.ArrayDequeue"); Set candidates = - MethodDispatchResolver.resolveAbstractDispatch(view, collectionSize); + MethodDispatchResolver.resolveAbstractDispatch(view, collectionSize) + .collect(Collectors.toSet()); assertTrue(collectionSize + " can resolve to " + setSize, candidates.contains(setSize)); assertTrue(collectionSize + " can resolve to " + listSize, candidates.contains(listSize)); assertTrue( abstractListSize + " can resolve to " + listSize, - MethodDispatchResolver.resolveAbstractDispatch(view, abstractListSize).contains(listSize)); + MethodDispatchResolver.resolveAbstractDispatch(view, abstractListSize) + .collect(Collectors.toSet()) + .contains(listSize)); assertTrue( objectClone + " can resolve to " + enumSetClone, - MethodDispatchResolver.resolveAbstractDispatch(view, objectClone).contains(enumSetClone)); + MethodDispatchResolver.resolveAbstractDispatch(view, objectClone) + .collect(Collectors.toSet()) + .contains(enumSetClone)); assertFalse( arrayDequeueClone + " cannot resolve to " + enumSetClone, MethodDispatchResolver.resolveAbstractDispatch(view, arrayDequeueClone) + .collect(Collectors.toSet()) .contains(enumSetClone)); } @@ -144,18 +149,23 @@ public void testCanDispatch() { objectWait1Param, objectWait2Param, view.getTypeHierarchy())); } - @Test(expected = ResolveException.class) + @Test public void invalidResolveConcreteDispatch() { IdentifierFactory factory = view.getIdentifierFactory(); - MethodDispatchResolver.resolveConcreteDispatch( - view, factory.parseMethodSignature("java.util.Collection#size(): int")); + Optional ms = + MethodDispatchResolver.resolveConcreteDispatch( + view, factory.parseMethodSignature("java.util.Collection#size(): int")); + assertFalse(ms.isPresent()); } - @Test(expected = ResolveException.class) + @Test() public void invalidResolveConcreteDispatchOfAbstractMethod() { IdentifierFactory factory = view.getIdentifierFactory(); - MethodDispatchResolver.resolveConcreteDispatch( - view, factory.parseMethodSignature("java.util.AbstractList#get(int): java.lang.Object")); + Optional ms = + MethodDispatchResolver.resolveConcreteDispatch( + view, + factory.parseMethodSignature("java.util.AbstractList#get(int): java.lang.Object")); + assertFalse(ms.isPresent()); } @Test diff --git a/sootup.tests/src/test/java/sootup/tests/typehierarchy/methoddispatchtestcase/AbstractDispatchTest.java b/sootup.tests/src/test/java/sootup/tests/typehierarchy/methoddispatchtestcase/AbstractDispatchTest.java index f3a1862d659..bf0260850ef 100644 --- a/sootup.tests/src/test/java/sootup/tests/typehierarchy/methoddispatchtestcase/AbstractDispatchTest.java +++ b/sootup.tests/src/test/java/sootup/tests/typehierarchy/methoddispatchtestcase/AbstractDispatchTest.java @@ -5,6 +5,7 @@ import categories.Java8Test; import java.util.Collections; import java.util.Set; +import java.util.stream.Collectors; import org.junit.Test; import org.junit.experimental.categories.Category; import sootup.core.signatures.MethodSignature; @@ -38,9 +39,10 @@ public void method() { Set candidatesAbstract = MethodDispatchResolver.resolveAbstractDispatch( - customTestWatcher.getView(), - identifierFactory.getMethodSignature( - sootClassTypeA, "method", "void", Collections.emptyList())); + customTestWatcher.getView(), + identifierFactory.getMethodSignature( + sootClassTypeA, "method", "void", Collections.emptyList())) + .collect(Collectors.toSet()); assertFalse(candidatesAbstract.contains(sootMethodAbstract)); assertFalse(candidatesAbstract.contains(sootMethodA)); assertFalse(candidatesAbstract.contains(sootMethodB)); @@ -48,9 +50,10 @@ public void method() { Set candidatesSuper = MethodDispatchResolver.resolveAbstractDispatch( - customTestWatcher.getView(), - identifierFactory.getMethodSignature( - sootClassTypeAbstract, "method", "void", Collections.emptyList())); + customTestWatcher.getView(), + identifierFactory.getMethodSignature( + sootClassTypeAbstract, "method", "void", Collections.emptyList())) + .collect(Collectors.toSet()); assertFalse(candidatesSuper.contains(sootMethodAbstract)); assertFalse(candidatesSuper.contains(sootMethodA)); assertFalse(candidatesSuper.contains(sootMethodB)); From 6889f0be265c3e840cda2a9f53d04b07741ea007 Mon Sep 17 00:00:00 2001 From: Jonas Klauke Date: Mon, 30 Oct 2023 18:15:51 +0100 Subject: [PATCH 02/23] removed unnecessary method. --- .../typehierarchy/MethodDispatchResolver.java | 28 ++++--------------- 1 file changed, 5 insertions(+), 23 deletions(-) diff --git a/sootup.core/src/main/java/sootup/core/typehierarchy/MethodDispatchResolver.java b/sootup.core/src/main/java/sootup/core/typehierarchy/MethodDispatchResolver.java index 817fde57144..814b5186bb1 100644 --- a/sootup.core/src/main/java/sootup/core/typehierarchy/MethodDispatchResolver.java +++ b/sootup.core/src/main/java/sootup/core/typehierarchy/MethodDispatchResolver.java @@ -217,6 +217,9 @@ public static Optional findConcreteMethod( } } Set interfaces = typeHierarchy.implementedInterfacesOf(sig.getDeclClassType()); + // interface1 is a sub-interface of interface2 + // interface1 is a super-interface of interface2 + // due to multiple inheritance in interfaces Optional defaultMethod = interfaces.stream() .map( @@ -225,7 +228,7 @@ public static Optional findConcreteMethod( identifierFactory.getMethodSignature(classType, sig.getSubSignature()))) .filter(Optional::isPresent) .map(Optional::get) - .sorted( + .min( (interface1, interface2) -> { // interface1 is a sub-interface of interface2 if (typeHierarchy.isSubtype( @@ -237,8 +240,7 @@ public static Optional findConcreteMethod( return 1; // due to multiple inheritance in interfaces return 0; - }) - .findFirst(); + }); if (defaultMethod.isPresent()) { return defaultMethod; } @@ -251,26 +253,6 @@ public static Optional findConcreteMethod( return Optional.empty(); } - /** - * Returns all SootClasses of interfaces that are implemented in the given SootClass - * - *

returns a list of all SootClass Objects of interfaces that are associated with the given - * sootClass parameter. The ClassTypes of the interfaces are converted to SootClasses and not - * contained Interfaces are filtered. - * - * @param view the view that contains all searched SootClasses - * @param sootClass it contains the interfaces - * @return a list of SootClasses of the interfaces of sootClass - */ - private static List> getSootClassesOfInterfaces( - View> view, SootClass sootClass) { - return sootClass.getInterfaces().stream() - .map(view::getClass) - .filter(Optional::isPresent) - .map(Optional::get) - .collect(Collectors.toList()); - } - /** * Resolves the actual method called by the specialInvokeExpr that is contained by * container. From 575a171c93310c74bd3a5bb3c4183b2ca77f994c Mon Sep 17 00:00:00 2001 From: Jonas Klauke Date: Mon, 30 Oct 2023 18:16:42 +0100 Subject: [PATCH 03/23] removed unnecessary method. --- .../java/sootup/callgraph/ClassHierarchyAnalysisAlgorithm.java | 1 + 1 file changed, 1 insertion(+) diff --git a/sootup.callgraph/src/main/java/sootup/callgraph/ClassHierarchyAnalysisAlgorithm.java b/sootup.callgraph/src/main/java/sootup/callgraph/ClassHierarchyAnalysisAlgorithm.java index 2eb2516ef62..8d47ee5834a 100644 --- a/sootup.callgraph/src/main/java/sootup/callgraph/ClassHierarchyAnalysisAlgorithm.java +++ b/sootup.callgraph/src/main/java/sootup/callgraph/ClassHierarchyAnalysisAlgorithm.java @@ -120,6 +120,7 @@ private Stream resolveAllSubClassCallTargets( // the super call target is a default method of an Interface. // If subtypes of class in the target signature does not implement the method, // other default method which are subtypes of the Interface can be targets + //TODO: add default method resolving } // the concrete target of the hierarchical highest class of call targets is known. From 32c22ac13bbcbd9975b6d07cf47661e8919daf25 Mon Sep 17 00:00:00 2001 From: Jonas Klauke Date: Mon, 13 Nov 2023 16:48:06 +0100 Subject: [PATCH 04/23] Created Comparartor for Hierarchy with Tests --- .../typehierarchy/HierarchyComparator.java | 55 ++++++ .../typehierarchy/MethodDispatchResolver.java | 16 +- .../HierarchyComparatorTest.java | 161 ++++++++++++++++++ .../Comparator/Interface.java | 3 + .../Comparator/SubInterface.java | 3 + .../Comparator/SubSubInterface.java | 3 + .../Comparator/SubSubclass.java | 3 + .../Comparator/Subclass.java | 3 + .../Comparator/Superclass.java | 3 + 9 files changed, 238 insertions(+), 12 deletions(-) create mode 100644 sootup.core/src/main/java/sootup/core/typehierarchy/HierarchyComparator.java create mode 100644 sootup.tests/src/test/java/sootup/tests/typehierarchy/HierarchyComparatorTest.java create mode 100644 sootup.tests/src/test/resources/javatypehierarchy/Comparator/Interface.java create mode 100644 sootup.tests/src/test/resources/javatypehierarchy/Comparator/SubInterface.java create mode 100644 sootup.tests/src/test/resources/javatypehierarchy/Comparator/SubSubInterface.java create mode 100644 sootup.tests/src/test/resources/javatypehierarchy/Comparator/SubSubclass.java create mode 100644 sootup.tests/src/test/resources/javatypehierarchy/Comparator/Subclass.java create mode 100644 sootup.tests/src/test/resources/javatypehierarchy/Comparator/Superclass.java diff --git a/sootup.core/src/main/java/sootup/core/typehierarchy/HierarchyComparator.java b/sootup.core/src/main/java/sootup/core/typehierarchy/HierarchyComparator.java new file mode 100644 index 00000000000..f2a2612ab02 --- /dev/null +++ b/sootup.core/src/main/java/sootup/core/typehierarchy/HierarchyComparator.java @@ -0,0 +1,55 @@ +package sootup.core.typehierarchy; +/*- + * #%L + * Soot - a J*va Optimization Framework + * %% + * Copyright (C) 2023 Jonas Klauke + * %% + * 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.Comparator; +import javax.annotation.Nonnull; +import sootup.core.model.SootClass; +import sootup.core.types.ClassType; +import sootup.core.views.View; + +/** + * Comparator to sort ClassTypes which are ideally connected in a Hierarchy. subtypes will be before + * super classes in the resulting order. + */ +public class HierarchyComparator implements Comparator { + + TypeHierarchy typeHierarchy; + + public HierarchyComparator(@Nonnull View> view) { + this(view.getTypeHierarchy()); + } + + public HierarchyComparator(@Nonnull TypeHierarchy hierarchy) { + this.typeHierarchy = hierarchy; + } + + @Override + public int compare(ClassType classType1, ClassType classType2) { + // classType1 is a subclass type of classType2 + if (typeHierarchy.isSubtype(classType2, classType1)) return -1; + // classType1 is a subclass type of classType2 + if (typeHierarchy.isSubtype(classType1, classType2)) return 1; + // classType1 and classType2 are on the same hierarchy level + return 0; + } +} diff --git a/sootup.core/src/main/java/sootup/core/typehierarchy/MethodDispatchResolver.java b/sootup.core/src/main/java/sootup/core/typehierarchy/MethodDispatchResolver.java index 814b5186bb1..018b1cac0e2 100644 --- a/sootup.core/src/main/java/sootup/core/typehierarchy/MethodDispatchResolver.java +++ b/sootup.core/src/main/java/sootup/core/typehierarchy/MethodDispatchResolver.java @@ -220,6 +220,7 @@ public static Optional findConcreteMethod( // interface1 is a sub-interface of interface2 // interface1 is a super-interface of interface2 // due to multiple inheritance in interfaces + final HierarchyComparator hierarchyComparator = new HierarchyComparator(view); Optional defaultMethod = interfaces.stream() .map( @@ -229,18 +230,9 @@ public static Optional findConcreteMethod( .filter(Optional::isPresent) .map(Optional::get) .min( - (interface1, interface2) -> { - // interface1 is a sub-interface of interface2 - if (typeHierarchy.isSubtype( - interface2.getDeclaringClassType(), interface1.getDeclaringClassType())) - return -1; - // interface1 is a super-interface of interface2 - if (typeHierarchy.isSubtype( - interface1.getDeclaringClassType(), interface2.getDeclaringClassType())) - return 1; - // due to multiple inheritance in interfaces - return 0; - }); + (m1, m2) -> + hierarchyComparator.compare( + m1.getDeclaringClassType(), m2.getDeclaringClassType())); if (defaultMethod.isPresent()) { return defaultMethod; } diff --git a/sootup.tests/src/test/java/sootup/tests/typehierarchy/HierarchyComparatorTest.java b/sootup.tests/src/test/java/sootup/tests/typehierarchy/HierarchyComparatorTest.java new file mode 100644 index 00000000000..fcb365709c0 --- /dev/null +++ b/sootup.tests/src/test/java/sootup/tests/typehierarchy/HierarchyComparatorTest.java @@ -0,0 +1,161 @@ +package sootup.tests.typehierarchy; + +import static org.junit.Assert.assertEquals; + +import categories.Java8Test; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import sootup.core.model.SootClass; +import sootup.core.typehierarchy.HierarchyComparator; +import sootup.core.types.ClassType; +import sootup.core.views.View; +import sootup.java.bytecode.inputlocation.JavaClassPathAnalysisInputLocation; +import sootup.java.core.JavaProject; +import sootup.java.core.language.JavaLanguage; +import sootup.java.sourcecode.inputlocation.JavaSourcePathAnalysisInputLocation; + +@Category(Java8Test.class) +public class HierarchyComparatorTest { + + private static View> view; + @BeforeClass + public static void setUp() { + JavaProject project = + JavaProject.builder(new JavaLanguage(8)) + .addInputLocation( + new JavaSourcePathAnalysisInputLocation( + Collections.singleton("src/test/resources/javatypehierarchy/Comparator"))) + .addInputLocation( + new JavaClassPathAnalysisInputLocation( + System.getProperty("java.home") + "/lib/rt.jar")) + .build(); + view = project.createView(); + } + @Test + public void testHierarchyComparatorOnClasses(){ + + ClassType superclass=view.getIdentifierFactory().getClassType("Superclass",""); + ClassType subclass=view.getIdentifierFactory().getClassType("Subclass",""); + ClassType subSubclass=view.getIdentifierFactory().getClassType("SubSubclass",""); + + HierarchyComparator hc=new HierarchyComparator(view); + assertEquals(-1,hc.compare(subclass,superclass)); + assertEquals(-1,hc.compare(subSubclass,superclass)); + assertEquals(-1,hc.compare(subSubclass,subclass)); + assertEquals(0,hc.compare(superclass,superclass)); + assertEquals(0,hc.compare(subclass,subclass)); + assertEquals(0,hc.compare(subSubclass,subSubclass)); + assertEquals(1,hc.compare(superclass,subclass)); + assertEquals(1,hc.compare(superclass,subSubclass)); + assertEquals(1,hc.compare(subclass,subSubclass)); + + ArrayList classes= new ArrayList<>(); + classes.add(superclass); + classes.add(subSubclass); + classes.add(subclass); + List classesSorted=classes.stream().sorted(hc).collect(Collectors.toList()); + assertEquals(subSubclass,classesSorted.get(0)); + assertEquals(subclass,classesSorted.get(1)); + assertEquals(superclass,classesSorted.get(2)); + } + @Test + public void testHierarchyComparatorOnInterfaces(){ + ClassType Interface=view.getIdentifierFactory().getClassType("Interface",""); + ClassType subInterface=view.getIdentifierFactory().getClassType("SubInterface",""); + ClassType subSubInterface2=view.getIdentifierFactory().getClassType("SubSubInterface",""); + + HierarchyComparator hc=new HierarchyComparator(view); + assertEquals(-1,hc.compare(subInterface,Interface)); + assertEquals(-1,hc.compare(subSubInterface2,Interface)); + assertEquals(-1,hc.compare(subSubInterface2,subInterface)); + assertEquals(0,hc.compare(Interface,Interface)); + assertEquals(0,hc.compare(subInterface,subInterface)); + assertEquals(0,hc.compare(subSubInterface2,subSubInterface2)); + assertEquals(1,hc.compare(Interface,subInterface)); + assertEquals(1,hc.compare(Interface,subSubInterface2)); + assertEquals(1,hc.compare(subInterface,subSubInterface2)); + + ArrayList classes= new ArrayList<>(); + classes.add(Interface); + classes.add(subSubInterface2); + classes.add(subInterface); + List classesSorted=classes.stream().sorted(hc).collect(Collectors.toList()); + assertEquals(subSubInterface2,classesSorted.get(0)); + assertEquals(subInterface,classesSorted.get(1)); + assertEquals(Interface,classesSorted.get(2)); + } + @Test + public void testHierarchyComparatorOnMixed(){ + ClassType Interface=view.getIdentifierFactory().getClassType("Interface",""); + ClassType subInterface=view.getIdentifierFactory().getClassType("SubInterface",""); + ClassType subSubInterface=view.getIdentifierFactory().getClassType("SubSubInterface",""); + ClassType superclass=view.getIdentifierFactory().getClassType("Superclass",""); + ClassType subclass=view.getIdentifierFactory().getClassType("Subclass",""); + ClassType subSubclass=view.getIdentifierFactory().getClassType("SubSubclass",""); + + HierarchyComparator hc=new HierarchyComparator(view); + + assertEquals(-1,hc.compare(subSubInterface,Interface)); + assertEquals(-1,hc.compare(subSubInterface,subInterface)); + assertEquals(0,hc.compare(subSubInterface,subSubInterface)); + assertEquals(1,hc.compare(subSubInterface,superclass)); + assertEquals(1,hc.compare(subSubInterface,subclass)); + assertEquals(1,hc.compare(subSubInterface,subSubclass)); + + assertEquals(-1,hc.compare(subInterface,Interface)); + assertEquals(0,hc.compare(subInterface,subInterface)); + assertEquals(1,hc.compare(subInterface,subSubInterface)); + assertEquals(1,hc.compare(subInterface,superclass)); + assertEquals(1,hc.compare(subInterface,subclass)); + assertEquals(1,hc.compare(subInterface,subSubclass)); + + assertEquals(0,hc.compare(Interface,Interface)); + assertEquals(1,hc.compare(Interface,subInterface)); + assertEquals(1,hc.compare(Interface,subSubInterface)); + assertEquals(1,hc.compare(Interface,superclass)); + assertEquals(1,hc.compare(Interface,subclass)); + assertEquals(1,hc.compare(Interface,subSubclass)); + + assertEquals(-1,hc.compare(superclass,Interface)); + assertEquals(-1,hc.compare(superclass,subInterface)); + assertEquals(-1,hc.compare(superclass,subSubInterface)); + assertEquals(0,hc.compare(superclass,superclass)); + assertEquals(1,hc.compare(superclass,subclass)); + assertEquals(1,hc.compare(superclass,subSubclass)); + + assertEquals(-1,hc.compare(subclass,Interface)); + assertEquals(-1,hc.compare(subclass,subInterface)); + assertEquals(-1,hc.compare(subclass,subSubInterface)); + assertEquals(-1,hc.compare(subclass,superclass)); + assertEquals(0,hc.compare(subclass,subclass)); + assertEquals(1,hc.compare(subclass,subSubclass)); + + assertEquals(-1,hc.compare(subSubclass,Interface)); + assertEquals(-1,hc.compare(subSubclass,subInterface)); + assertEquals(-1,hc.compare(subSubclass,subSubInterface)); + assertEquals(-1,hc.compare(subSubclass,superclass)); + assertEquals(-1,hc.compare(subSubclass,subclass)); + assertEquals(0,hc.compare(subSubclass,subSubclass)); + + + ArrayList classes= new ArrayList<>(); + classes.add(Interface); + classes.add(subclass); + classes.add(subSubInterface); + classes.add(superclass); + classes.add(subInterface); + classes.add(subSubclass); + List classesSorted=classes.stream().sorted(hc).collect(Collectors.toList()); + assertEquals(subSubclass,classesSorted.get(0)); + assertEquals(subclass,classesSorted.get(1)); + assertEquals(superclass,classesSorted.get(2)); + assertEquals(subSubInterface,classesSorted.get(3)); + assertEquals(subInterface,classesSorted.get(4)); + assertEquals(Interface,classesSorted.get(5)); + } +} diff --git a/sootup.tests/src/test/resources/javatypehierarchy/Comparator/Interface.java b/sootup.tests/src/test/resources/javatypehierarchy/Comparator/Interface.java new file mode 100644 index 00000000000..326a0e6bca3 --- /dev/null +++ b/sootup.tests/src/test/resources/javatypehierarchy/Comparator/Interface.java @@ -0,0 +1,3 @@ +interface Interface{ + +} \ No newline at end of file diff --git a/sootup.tests/src/test/resources/javatypehierarchy/Comparator/SubInterface.java b/sootup.tests/src/test/resources/javatypehierarchy/Comparator/SubInterface.java new file mode 100644 index 00000000000..04920dfd902 --- /dev/null +++ b/sootup.tests/src/test/resources/javatypehierarchy/Comparator/SubInterface.java @@ -0,0 +1,3 @@ +interface SubInterface extends Interface{ + +} \ No newline at end of file diff --git a/sootup.tests/src/test/resources/javatypehierarchy/Comparator/SubSubInterface.java b/sootup.tests/src/test/resources/javatypehierarchy/Comparator/SubSubInterface.java new file mode 100644 index 00000000000..2b1a50eb17f --- /dev/null +++ b/sootup.tests/src/test/resources/javatypehierarchy/Comparator/SubSubInterface.java @@ -0,0 +1,3 @@ +interface SubSubInterface extends SubInterface{ + +} \ No newline at end of file diff --git a/sootup.tests/src/test/resources/javatypehierarchy/Comparator/SubSubclass.java b/sootup.tests/src/test/resources/javatypehierarchy/Comparator/SubSubclass.java new file mode 100644 index 00000000000..a325111b873 --- /dev/null +++ b/sootup.tests/src/test/resources/javatypehierarchy/Comparator/SubSubclass.java @@ -0,0 +1,3 @@ +class SubSubclass extends Subclass{ + +} \ No newline at end of file diff --git a/sootup.tests/src/test/resources/javatypehierarchy/Comparator/Subclass.java b/sootup.tests/src/test/resources/javatypehierarchy/Comparator/Subclass.java new file mode 100644 index 00000000000..4567627f2b1 --- /dev/null +++ b/sootup.tests/src/test/resources/javatypehierarchy/Comparator/Subclass.java @@ -0,0 +1,3 @@ +class Subclass extends Superclass{ + +} \ No newline at end of file diff --git a/sootup.tests/src/test/resources/javatypehierarchy/Comparator/Superclass.java b/sootup.tests/src/test/resources/javatypehierarchy/Comparator/Superclass.java new file mode 100644 index 00000000000..491f0e44eb0 --- /dev/null +++ b/sootup.tests/src/test/resources/javatypehierarchy/Comparator/Superclass.java @@ -0,0 +1,3 @@ +class Superclass implements SubSubInterface{ + +} \ No newline at end of file From 9a03be00cb4f41dce1fc069fa23177a14f9a5c75 Mon Sep 17 00:00:00 2001 From: Jonas Klauke Date: Mon, 13 Nov 2023 20:27:20 +0100 Subject: [PATCH 05/23] fixed interface test that used wrong method names --- .../src/test/java/sootup/callgraph/CallGraphTestBase.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sootup.callgraph/src/test/java/sootup/callgraph/CallGraphTestBase.java b/sootup.callgraph/src/test/java/sootup/callgraph/CallGraphTestBase.java index 647c9ba63c7..6a986c65e8b 100644 --- a/sootup.callgraph/src/test/java/sootup/callgraph/CallGraphTestBase.java +++ b/sootup.callgraph/src/test/java/sootup/callgraph/CallGraphTestBase.java @@ -165,13 +165,13 @@ public void testConcreteCallDifferentDefaultMethodInSubClass() { MethodSignature interfaceMethod = identifierFactory.getMethodSignature( identifierFactory.getClassType("cvcscddi.Interface"), - "method", + "target", "void", Collections.emptyList()); MethodSignature subInterfaceMethod = identifierFactory.getMethodSignature( identifierFactory.getClassType("cvcscddi.SubInterface"), - "method", + "target", "void", Collections.emptyList()); assertTrue(cg.containsCall(mainMethodSignature, interfaceMethod)); From a400864cd49a4112bba12ff8bc806df744edd007 Mon Sep 17 00:00:00 2001 From: Jonas Klauke Date: Mon, 13 Nov 2023 20:30:06 +0100 Subject: [PATCH 06/23] fmt --- .../HierarchyComparatorTest.java | 210 +++++++++--------- 1 file changed, 106 insertions(+), 104 deletions(-) diff --git a/sootup.tests/src/test/java/sootup/tests/typehierarchy/HierarchyComparatorTest.java b/sootup.tests/src/test/java/sootup/tests/typehierarchy/HierarchyComparatorTest.java index fcb365709c0..2010f2a7a01 100644 --- a/sootup.tests/src/test/java/sootup/tests/typehierarchy/HierarchyComparatorTest.java +++ b/sootup.tests/src/test/java/sootup/tests/typehierarchy/HierarchyComparatorTest.java @@ -23,6 +23,7 @@ public class HierarchyComparatorTest { private static View> view; + @BeforeClass public static void setUp() { JavaProject project = @@ -36,126 +37,127 @@ public static void setUp() { .build(); view = project.createView(); } + @Test - public void testHierarchyComparatorOnClasses(){ - - ClassType superclass=view.getIdentifierFactory().getClassType("Superclass",""); - ClassType subclass=view.getIdentifierFactory().getClassType("Subclass",""); - ClassType subSubclass=view.getIdentifierFactory().getClassType("SubSubclass",""); - - HierarchyComparator hc=new HierarchyComparator(view); - assertEquals(-1,hc.compare(subclass,superclass)); - assertEquals(-1,hc.compare(subSubclass,superclass)); - assertEquals(-1,hc.compare(subSubclass,subclass)); - assertEquals(0,hc.compare(superclass,superclass)); - assertEquals(0,hc.compare(subclass,subclass)); - assertEquals(0,hc.compare(subSubclass,subSubclass)); - assertEquals(1,hc.compare(superclass,subclass)); - assertEquals(1,hc.compare(superclass,subSubclass)); - assertEquals(1,hc.compare(subclass,subSubclass)); - - ArrayList classes= new ArrayList<>(); + public void testHierarchyComparatorOnClasses() { + ClassType superclass = view.getIdentifierFactory().getClassType("Superclass", ""); + ClassType subclass = view.getIdentifierFactory().getClassType("Subclass", ""); + ClassType subSubclass = view.getIdentifierFactory().getClassType("SubSubclass", ""); + + HierarchyComparator hc = new HierarchyComparator(view); + assertEquals(-1, hc.compare(subclass, superclass)); + assertEquals(-1, hc.compare(subSubclass, superclass)); + assertEquals(-1, hc.compare(subSubclass, subclass)); + assertEquals(0, hc.compare(superclass, superclass)); + assertEquals(0, hc.compare(subclass, subclass)); + assertEquals(0, hc.compare(subSubclass, subSubclass)); + assertEquals(1, hc.compare(superclass, subclass)); + assertEquals(1, hc.compare(superclass, subSubclass)); + assertEquals(1, hc.compare(subclass, subSubclass)); + + ArrayList classes = new ArrayList<>(); classes.add(superclass); classes.add(subSubclass); classes.add(subclass); - List classesSorted=classes.stream().sorted(hc).collect(Collectors.toList()); - assertEquals(subSubclass,classesSorted.get(0)); - assertEquals(subclass,classesSorted.get(1)); - assertEquals(superclass,classesSorted.get(2)); + List classesSorted = classes.stream().sorted(hc).collect(Collectors.toList()); + assertEquals(subSubclass, classesSorted.get(0)); + assertEquals(subclass, classesSorted.get(1)); + assertEquals(superclass, classesSorted.get(2)); } + @Test - public void testHierarchyComparatorOnInterfaces(){ - ClassType Interface=view.getIdentifierFactory().getClassType("Interface",""); - ClassType subInterface=view.getIdentifierFactory().getClassType("SubInterface",""); - ClassType subSubInterface2=view.getIdentifierFactory().getClassType("SubSubInterface",""); - - HierarchyComparator hc=new HierarchyComparator(view); - assertEquals(-1,hc.compare(subInterface,Interface)); - assertEquals(-1,hc.compare(subSubInterface2,Interface)); - assertEquals(-1,hc.compare(subSubInterface2,subInterface)); - assertEquals(0,hc.compare(Interface,Interface)); - assertEquals(0,hc.compare(subInterface,subInterface)); - assertEquals(0,hc.compare(subSubInterface2,subSubInterface2)); - assertEquals(1,hc.compare(Interface,subInterface)); - assertEquals(1,hc.compare(Interface,subSubInterface2)); - assertEquals(1,hc.compare(subInterface,subSubInterface2)); - - ArrayList classes= new ArrayList<>(); + public void testHierarchyComparatorOnInterfaces() { + ClassType Interface = view.getIdentifierFactory().getClassType("Interface", ""); + ClassType subInterface = view.getIdentifierFactory().getClassType("SubInterface", ""); + ClassType subSubInterface2 = view.getIdentifierFactory().getClassType("SubSubInterface", ""); + + HierarchyComparator hc = new HierarchyComparator(view); + assertEquals(-1, hc.compare(subInterface, Interface)); + assertEquals(-1, hc.compare(subSubInterface2, Interface)); + assertEquals(-1, hc.compare(subSubInterface2, subInterface)); + assertEquals(0, hc.compare(Interface, Interface)); + assertEquals(0, hc.compare(subInterface, subInterface)); + assertEquals(0, hc.compare(subSubInterface2, subSubInterface2)); + assertEquals(1, hc.compare(Interface, subInterface)); + assertEquals(1, hc.compare(Interface, subSubInterface2)); + assertEquals(1, hc.compare(subInterface, subSubInterface2)); + + ArrayList classes = new ArrayList<>(); classes.add(Interface); classes.add(subSubInterface2); classes.add(subInterface); - List classesSorted=classes.stream().sorted(hc).collect(Collectors.toList()); - assertEquals(subSubInterface2,classesSorted.get(0)); - assertEquals(subInterface,classesSorted.get(1)); - assertEquals(Interface,classesSorted.get(2)); + List classesSorted = classes.stream().sorted(hc).collect(Collectors.toList()); + assertEquals(subSubInterface2, classesSorted.get(0)); + assertEquals(subInterface, classesSorted.get(1)); + assertEquals(Interface, classesSorted.get(2)); } + @Test - public void testHierarchyComparatorOnMixed(){ - ClassType Interface=view.getIdentifierFactory().getClassType("Interface",""); - ClassType subInterface=view.getIdentifierFactory().getClassType("SubInterface",""); - ClassType subSubInterface=view.getIdentifierFactory().getClassType("SubSubInterface",""); - ClassType superclass=view.getIdentifierFactory().getClassType("Superclass",""); - ClassType subclass=view.getIdentifierFactory().getClassType("Subclass",""); - ClassType subSubclass=view.getIdentifierFactory().getClassType("SubSubclass",""); - - HierarchyComparator hc=new HierarchyComparator(view); - - assertEquals(-1,hc.compare(subSubInterface,Interface)); - assertEquals(-1,hc.compare(subSubInterface,subInterface)); - assertEquals(0,hc.compare(subSubInterface,subSubInterface)); - assertEquals(1,hc.compare(subSubInterface,superclass)); - assertEquals(1,hc.compare(subSubInterface,subclass)); - assertEquals(1,hc.compare(subSubInterface,subSubclass)); - - assertEquals(-1,hc.compare(subInterface,Interface)); - assertEquals(0,hc.compare(subInterface,subInterface)); - assertEquals(1,hc.compare(subInterface,subSubInterface)); - assertEquals(1,hc.compare(subInterface,superclass)); - assertEquals(1,hc.compare(subInterface,subclass)); - assertEquals(1,hc.compare(subInterface,subSubclass)); - - assertEquals(0,hc.compare(Interface,Interface)); - assertEquals(1,hc.compare(Interface,subInterface)); - assertEquals(1,hc.compare(Interface,subSubInterface)); - assertEquals(1,hc.compare(Interface,superclass)); - assertEquals(1,hc.compare(Interface,subclass)); - assertEquals(1,hc.compare(Interface,subSubclass)); - - assertEquals(-1,hc.compare(superclass,Interface)); - assertEquals(-1,hc.compare(superclass,subInterface)); - assertEquals(-1,hc.compare(superclass,subSubInterface)); - assertEquals(0,hc.compare(superclass,superclass)); - assertEquals(1,hc.compare(superclass,subclass)); - assertEquals(1,hc.compare(superclass,subSubclass)); - - assertEquals(-1,hc.compare(subclass,Interface)); - assertEquals(-1,hc.compare(subclass,subInterface)); - assertEquals(-1,hc.compare(subclass,subSubInterface)); - assertEquals(-1,hc.compare(subclass,superclass)); - assertEquals(0,hc.compare(subclass,subclass)); - assertEquals(1,hc.compare(subclass,subSubclass)); - - assertEquals(-1,hc.compare(subSubclass,Interface)); - assertEquals(-1,hc.compare(subSubclass,subInterface)); - assertEquals(-1,hc.compare(subSubclass,subSubInterface)); - assertEquals(-1,hc.compare(subSubclass,superclass)); - assertEquals(-1,hc.compare(subSubclass,subclass)); - assertEquals(0,hc.compare(subSubclass,subSubclass)); - - - ArrayList classes= new ArrayList<>(); + public void testHierarchyComparatorOnMixed() { + ClassType Interface = view.getIdentifierFactory().getClassType("Interface", ""); + ClassType subInterface = view.getIdentifierFactory().getClassType("SubInterface", ""); + ClassType subSubInterface = view.getIdentifierFactory().getClassType("SubSubInterface", ""); + ClassType superclass = view.getIdentifierFactory().getClassType("Superclass", ""); + ClassType subclass = view.getIdentifierFactory().getClassType("Subclass", ""); + ClassType subSubclass = view.getIdentifierFactory().getClassType("SubSubclass", ""); + + HierarchyComparator hc = new HierarchyComparator(view); + + assertEquals(-1, hc.compare(subSubInterface, Interface)); + assertEquals(-1, hc.compare(subSubInterface, subInterface)); + assertEquals(0, hc.compare(subSubInterface, subSubInterface)); + assertEquals(1, hc.compare(subSubInterface, superclass)); + assertEquals(1, hc.compare(subSubInterface, subclass)); + assertEquals(1, hc.compare(subSubInterface, subSubclass)); + + assertEquals(-1, hc.compare(subInterface, Interface)); + assertEquals(0, hc.compare(subInterface, subInterface)); + assertEquals(1, hc.compare(subInterface, subSubInterface)); + assertEquals(1, hc.compare(subInterface, superclass)); + assertEquals(1, hc.compare(subInterface, subclass)); + assertEquals(1, hc.compare(subInterface, subSubclass)); + + assertEquals(0, hc.compare(Interface, Interface)); + assertEquals(1, hc.compare(Interface, subInterface)); + assertEquals(1, hc.compare(Interface, subSubInterface)); + assertEquals(1, hc.compare(Interface, superclass)); + assertEquals(1, hc.compare(Interface, subclass)); + assertEquals(1, hc.compare(Interface, subSubclass)); + + assertEquals(-1, hc.compare(superclass, Interface)); + assertEquals(-1, hc.compare(superclass, subInterface)); + assertEquals(-1, hc.compare(superclass, subSubInterface)); + assertEquals(0, hc.compare(superclass, superclass)); + assertEquals(1, hc.compare(superclass, subclass)); + assertEquals(1, hc.compare(superclass, subSubclass)); + + assertEquals(-1, hc.compare(subclass, Interface)); + assertEquals(-1, hc.compare(subclass, subInterface)); + assertEquals(-1, hc.compare(subclass, subSubInterface)); + assertEquals(-1, hc.compare(subclass, superclass)); + assertEquals(0, hc.compare(subclass, subclass)); + assertEquals(1, hc.compare(subclass, subSubclass)); + + assertEquals(-1, hc.compare(subSubclass, Interface)); + assertEquals(-1, hc.compare(subSubclass, subInterface)); + assertEquals(-1, hc.compare(subSubclass, subSubInterface)); + assertEquals(-1, hc.compare(subSubclass, superclass)); + assertEquals(-1, hc.compare(subSubclass, subclass)); + assertEquals(0, hc.compare(subSubclass, subSubclass)); + + ArrayList classes = new ArrayList<>(); classes.add(Interface); classes.add(subclass); classes.add(subSubInterface); classes.add(superclass); classes.add(subInterface); classes.add(subSubclass); - List classesSorted=classes.stream().sorted(hc).collect(Collectors.toList()); - assertEquals(subSubclass,classesSorted.get(0)); - assertEquals(subclass,classesSorted.get(1)); - assertEquals(superclass,classesSorted.get(2)); - assertEquals(subSubInterface,classesSorted.get(3)); - assertEquals(subInterface,classesSorted.get(4)); - assertEquals(Interface,classesSorted.get(5)); + List classesSorted = classes.stream().sorted(hc).collect(Collectors.toList()); + assertEquals(subSubclass, classesSorted.get(0)); + assertEquals(subclass, classesSorted.get(1)); + assertEquals(superclass, classesSorted.get(2)); + assertEquals(subSubInterface, classesSorted.get(3)); + assertEquals(subInterface, classesSorted.get(4)); + assertEquals(Interface, classesSorted.get(5)); } } From 7883bd3569d0abf205ff0716d118f8b2784c33fc Mon Sep 17 00:00:00 2001 From: Jonas Klauke Date: Mon, 13 Nov 2023 20:31:31 +0100 Subject: [PATCH 07/23] improved CHA call graph algorithm --- .../ClassHierarchyAnalysisAlgorithm.java | 68 ++++++++++--------- 1 file changed, 36 insertions(+), 32 deletions(-) diff --git a/sootup.callgraph/src/main/java/sootup/callgraph/ClassHierarchyAnalysisAlgorithm.java b/sootup.callgraph/src/main/java/sootup/callgraph/ClassHierarchyAnalysisAlgorithm.java index 8d47ee5834a..3411060b63e 100644 --- a/sootup.callgraph/src/main/java/sootup/callgraph/ClassHierarchyAnalysisAlgorithm.java +++ b/sootup.callgraph/src/main/java/sootup/callgraph/ClassHierarchyAnalysisAlgorithm.java @@ -90,42 +90,46 @@ protected Stream resolveCall(SootMethod method, AbstractInvokeE || (invokeExpr instanceof JSpecialInvokeExpr)) { return Stream.of(targetMethodSignature); } else { - if (targetMethod.isAbstract() || invokeExpr instanceof JInterfaceInvokeExpr) { - // abstract method call or interface call - // TODO: SpeedUp by only resolving direct subclasses - return resolveAllCallTargets(targetMethodSignature); + List targets = resolveAllCallTargets(targetMethodSignature); + if (!targetMethod.isAbstract()) { + targets.add(targetMethod.getSignature()); } - return Stream.concat( - Stream.of(targetMethod.getSignature()), - resolveAllSubClassCallTargets(targetMethodSignature, targetMethod)); + if (invokeExpr instanceof JInterfaceInvokeExpr) { + // TODO:add concrete dispatches of subtypes with no implemented + } + return targets.stream(); } } - private Stream resolveAllCallTargets(MethodSignature targetMethodSignature) { - return MethodDispatchResolver.resolveAllDispatches(view, targetMethodSignature).stream() - .map( - methodSignature -> - MethodDispatchResolver.resolveConcreteDispatch(view, methodSignature)) - .filter(Optional::isPresent) - .map(Optional::get); - } - - private Stream resolveAllSubClassCallTargets( - MethodSignature targetMethodSignature, SootMethod targetMethod) { - SootClass sootClass = view.getClass(targetMethod.getDeclaringClassType()).orElse(null); - if (sootClass == null) { - return Stream.empty(); - } - if (sootClass.isInterface()) { - // the super call target is a default method of an Interface. - // If subtypes of class in the target signature does not implement the method, - // other default method which are subtypes of the Interface can be targets - //TODO: add default method resolving - } - - // the concrete target of the hierarchical highest class of call targets is known. - // this method can be only overwritten by implemented methods of the subtypes - return MethodDispatchResolver.resolveAbstractDispatch(view, targetMethodSignature); + private List resolveAllCallTargets(MethodSignature targetMethodSignature) { + ArrayList targets = new ArrayList<>(); + view.getTypeHierarchy() + .subtypesOf(targetMethodSignature.getDeclClassType()) + .forEach( + classType -> { + SootClass clazz = view.getClass(classType).orElse(null); + if (clazz == null) return; + // check if method is implemented + SootMethod method = + clazz.getMethod(targetMethodSignature.getSubSignature()).orElse(null); + if (method != null && !method.isAbstract()) targets.add(method.getSignature()); + // collect all default methods + clazz + .getInterfaces() + .forEach( + interfaceType -> { + SootMethod defaultMethod = + view.getMethod( + view.getIdentifierFactory() + .getMethodSignature( + interfaceType, targetMethodSignature.getSubSignature())) + .orElse(null); + // contains an implemented default method + if (defaultMethod != null && !defaultMethod.isAbstract()) + targets.add(defaultMethod.getSignature()); + }); + }); + return targets; } @Override From a66449332e1d4ab4f58ab437e3c53638201a273a Mon Sep 17 00:00:00 2001 From: Jonas Klauke Date: Mon, 13 Nov 2023 20:35:37 +0100 Subject: [PATCH 08/23] removed compare call tests of two equal class types --- .../tests/typehierarchy/HierarchyComparatorTest.java | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/sootup.tests/src/test/java/sootup/tests/typehierarchy/HierarchyComparatorTest.java b/sootup.tests/src/test/java/sootup/tests/typehierarchy/HierarchyComparatorTest.java index 2010f2a7a01..e431486ae2d 100644 --- a/sootup.tests/src/test/java/sootup/tests/typehierarchy/HierarchyComparatorTest.java +++ b/sootup.tests/src/test/java/sootup/tests/typehierarchy/HierarchyComparatorTest.java @@ -48,9 +48,6 @@ public void testHierarchyComparatorOnClasses() { assertEquals(-1, hc.compare(subclass, superclass)); assertEquals(-1, hc.compare(subSubclass, superclass)); assertEquals(-1, hc.compare(subSubclass, subclass)); - assertEquals(0, hc.compare(superclass, superclass)); - assertEquals(0, hc.compare(subclass, subclass)); - assertEquals(0, hc.compare(subSubclass, subSubclass)); assertEquals(1, hc.compare(superclass, subclass)); assertEquals(1, hc.compare(superclass, subSubclass)); assertEquals(1, hc.compare(subclass, subSubclass)); @@ -75,9 +72,6 @@ public void testHierarchyComparatorOnInterfaces() { assertEquals(-1, hc.compare(subInterface, Interface)); assertEquals(-1, hc.compare(subSubInterface2, Interface)); assertEquals(-1, hc.compare(subSubInterface2, subInterface)); - assertEquals(0, hc.compare(Interface, Interface)); - assertEquals(0, hc.compare(subInterface, subInterface)); - assertEquals(0, hc.compare(subSubInterface2, subSubInterface2)); assertEquals(1, hc.compare(Interface, subInterface)); assertEquals(1, hc.compare(Interface, subSubInterface2)); assertEquals(1, hc.compare(subInterface, subSubInterface2)); @@ -105,19 +99,16 @@ public void testHierarchyComparatorOnMixed() { assertEquals(-1, hc.compare(subSubInterface, Interface)); assertEquals(-1, hc.compare(subSubInterface, subInterface)); - assertEquals(0, hc.compare(subSubInterface, subSubInterface)); assertEquals(1, hc.compare(subSubInterface, superclass)); assertEquals(1, hc.compare(subSubInterface, subclass)); assertEquals(1, hc.compare(subSubInterface, subSubclass)); assertEquals(-1, hc.compare(subInterface, Interface)); - assertEquals(0, hc.compare(subInterface, subInterface)); assertEquals(1, hc.compare(subInterface, subSubInterface)); assertEquals(1, hc.compare(subInterface, superclass)); assertEquals(1, hc.compare(subInterface, subclass)); assertEquals(1, hc.compare(subInterface, subSubclass)); - assertEquals(0, hc.compare(Interface, Interface)); assertEquals(1, hc.compare(Interface, subInterface)); assertEquals(1, hc.compare(Interface, subSubInterface)); assertEquals(1, hc.compare(Interface, superclass)); @@ -127,7 +118,6 @@ public void testHierarchyComparatorOnMixed() { assertEquals(-1, hc.compare(superclass, Interface)); assertEquals(-1, hc.compare(superclass, subInterface)); assertEquals(-1, hc.compare(superclass, subSubInterface)); - assertEquals(0, hc.compare(superclass, superclass)); assertEquals(1, hc.compare(superclass, subclass)); assertEquals(1, hc.compare(superclass, subSubclass)); @@ -135,7 +125,6 @@ public void testHierarchyComparatorOnMixed() { assertEquals(-1, hc.compare(subclass, subInterface)); assertEquals(-1, hc.compare(subclass, subSubInterface)); assertEquals(-1, hc.compare(subclass, superclass)); - assertEquals(0, hc.compare(subclass, subclass)); assertEquals(1, hc.compare(subclass, subSubclass)); assertEquals(-1, hc.compare(subSubclass, Interface)); @@ -143,7 +132,6 @@ public void testHierarchyComparatorOnMixed() { assertEquals(-1, hc.compare(subSubclass, subSubInterface)); assertEquals(-1, hc.compare(subSubclass, superclass)); assertEquals(-1, hc.compare(subSubclass, subclass)); - assertEquals(0, hc.compare(subSubclass, subSubclass)); ArrayList classes = new ArrayList<>(); classes.add(Interface); From 6772b96ce73b82a748939fcef4cef2b5687ee96d Mon Sep 17 00:00:00 2001 From: Jonas Klauke Date: Tue, 14 Nov 2023 10:15:36 +0100 Subject: [PATCH 09/23] fixed Typo --- .../ConcreteCall/binary/ConcreteCallDefaultInterface.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sootup.callgraph/src/test/resources/callgraph/ConcreteCall/binary/ConcreteCallDefaultInterface.java b/sootup.callgraph/src/test/resources/callgraph/ConcreteCall/binary/ConcreteCallDefaultInterface.java index 2f4d832ab5b..9db1421d553 100644 --- a/sootup.callgraph/src/test/resources/callgraph/ConcreteCall/binary/ConcreteCallDefaultInterface.java +++ b/sootup.callgraph/src/test/resources/callgraph/ConcreteCall/binary/ConcreteCallDefaultInterface.java @@ -1,7 +1,7 @@ // cvci/Class.java package cvci; -class Class implements SInterface{ +class Class implements Interface{ public static void main(String[] args){ Class cls = new Class(); From a3fb26bee70bf7924d20cc61036a728de2b7318b Mon Sep 17 00:00:00 2001 From: Jonas Klauke Date: Tue, 14 Nov 2023 10:17:21 +0100 Subject: [PATCH 10/23] added concrete method resolving of classes with no implementation if the invoke is an interface invoke --- .../ClassHierarchyAnalysisAlgorithm.java | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/sootup.callgraph/src/main/java/sootup/callgraph/ClassHierarchyAnalysisAlgorithm.java b/sootup.callgraph/src/main/java/sootup/callgraph/ClassHierarchyAnalysisAlgorithm.java index 3411060b63e..b9404432d97 100644 --- a/sootup.callgraph/src/main/java/sootup/callgraph/ClassHierarchyAnalysisAlgorithm.java +++ b/sootup.callgraph/src/main/java/sootup/callgraph/ClassHierarchyAnalysisAlgorithm.java @@ -25,6 +25,7 @@ import java.util.*; import java.util.stream.Stream; import javax.annotation.Nonnull; +import sootup.core.IdentifierFactory; import sootup.core.jimple.common.expr.AbstractInvokeExpr; import sootup.core.jimple.common.expr.JDynamicInvokeExpr; import sootup.core.jimple.common.expr.JInterfaceInvokeExpr; @@ -34,6 +35,7 @@ import sootup.core.model.SootMethod; import sootup.core.signatures.MethodSignature; import sootup.core.typehierarchy.MethodDispatchResolver; +import sootup.core.types.ClassType; import sootup.core.views.View; /** @@ -90,18 +92,31 @@ protected Stream resolveCall(SootMethod method, AbstractInvokeE || (invokeExpr instanceof JSpecialInvokeExpr)) { return Stream.of(targetMethodSignature); } else { - List targets = resolveAllCallTargets(targetMethodSignature); + ArrayList noImplementedMethod = new ArrayList<>(); + List targets = + resolveAllCallTargets(targetMethodSignature, noImplementedMethod); if (!targetMethod.isAbstract()) { targets.add(targetMethod.getSignature()); } if (invokeExpr instanceof JInterfaceInvokeExpr) { - // TODO:add concrete dispatches of subtypes with no implemented + IdentifierFactory factory = view.getIdentifierFactory(); + noImplementedMethod.stream() + .map( + classType -> + MethodDispatchResolver.resolveConcreteDispatch( + view, + factory.getMethodSignature( + classType, targetMethodSignature.getSubSignature()))) + .filter(Optional::isPresent) + .map(Optional::get) + .forEach(targets::add); } return targets.stream(); } } - private List resolveAllCallTargets(MethodSignature targetMethodSignature) { + private List resolveAllCallTargets( + MethodSignature targetMethodSignature, ArrayList noImplementedMethod) { ArrayList targets = new ArrayList<>(); view.getTypeHierarchy() .subtypesOf(targetMethodSignature.getDeclClassType()) @@ -113,6 +128,8 @@ private List resolveAllCallTargets(MethodSignature targetMethod SootMethod method = clazz.getMethod(targetMethodSignature.getSubSignature()).orElse(null); if (method != null && !method.isAbstract()) targets.add(method.getSignature()); + // save classes with no implementation of the searched method + if (method == null && !clazz.isInterface()) noImplementedMethod.add(classType); // collect all default methods clazz .getInterfaces() From a4db7b1e46b81c8529aae4129e153ec19dd32a92 Mon Sep 17 00:00:00 2001 From: Jonas Klauke Date: Tue, 14 Nov 2023 13:51:52 +0100 Subject: [PATCH 11/23] added missed method name refactoring after merge --- .../java/sootup/core/typehierarchy/MethodDispatchResolver.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sootup.core/src/main/java/sootup/core/typehierarchy/MethodDispatchResolver.java b/sootup.core/src/main/java/sootup/core/typehierarchy/MethodDispatchResolver.java index 018b1cac0e2..99564b545fb 100644 --- a/sootup.core/src/main/java/sootup/core/typehierarchy/MethodDispatchResolver.java +++ b/sootup.core/src/main/java/sootup/core/typehierarchy/MethodDispatchResolver.java @@ -207,7 +207,7 @@ public static Optional findConcreteMethod( } TypeHierarchy typeHierarchy = view.getTypeHierarchy(); - List superClasses = typeHierarchy.incompleteSuperClassesOf(sig.getDeclClassType()); + List superClasses = typeHierarchy.superClassesOf(sig.getDeclClassType()); for (ClassType superClassType : superClasses) { Optional method = view.getMethod( From f79795e48972127d24867b24e649c45cf03df030 Mon Sep 17 00:00:00 2001 From: Jonas Klauke Date: Tue, 14 Nov 2023 14:33:49 +0100 Subject: [PATCH 12/23] shrank down unnecessary utility class MethodDispatchResolver. Removed unnecessary tests. Moved utility methods to the actual class using them. moved tests. --- .../interprocedural/icfg/ICFGDotExporter.java | 3 +- .../callgraph/AbstractCallGraphAlgorithm.java | 76 ++++++ .../ClassHierarchyAnalysisAlgorithm.java | 5 +- .../callgraph/RapidTypeAnalysisAlgorithm.java | 8 +- .../callgraph/ConcreteDispatchTest.java | 188 ++++++++++++++ .../callgraph}/ConcreteDispatch/A.java | 0 .../callgraph}/ConcreteDispatch/B.java | 0 .../callgraph}/ConcreteDispatch/C.java | 0 .../callgraph}/ConcreteDispatch/D.java | 0 .../callgraph}/ConcreteDispatch/I.java | 0 .../callgraph}/ConcreteDispatch/J.java | 0 .../typehierarchy/MethodDispatchResolver.java | 166 ------------- .../typehierarchy/MethodDispatchBase.java | 78 ------ .../MethodDispatchResolverTest.java | 235 ------------------ .../AbstractDispatchTest.java | 62 ----- .../AllDispatchTest.java | 46 ---- .../ConcreteDispatchTest.java | 107 -------- .../CovarianceDispatchTest.java | 45 ---- .../AbstractDispatch/A.java | 8 - .../AbstractDispatch/AbstractClass.java | 6 - .../AbstractDispatch/B.java | 5 - .../AbstractDispatch/C.java | 8 - .../methoddispatchresolver/AllDispatch/A.java | 7 - .../AllDispatch/AbstractClass.java | 6 - .../methoddispatchresolver/AllDispatch/B.java | 5 - .../methoddispatchresolver/AllDispatch/C.java | 8 - .../CovarianceDispatch/Subclass.java | 8 - .../CovarianceDispatch/Superclass.java | 8 - 28 files changed, 271 insertions(+), 817 deletions(-) create mode 100644 sootup.callgraph/src/test/java/sootup/callgraph/ConcreteDispatchTest.java rename {sootup.tests/src/test/resources/methoddispatchresolver => sootup.callgraph/src/test/resources/callgraph}/ConcreteDispatch/A.java (100%) rename {sootup.tests/src/test/resources/methoddispatchresolver => sootup.callgraph/src/test/resources/callgraph}/ConcreteDispatch/B.java (100%) rename {sootup.tests/src/test/resources/methoddispatchresolver => sootup.callgraph/src/test/resources/callgraph}/ConcreteDispatch/C.java (100%) rename {sootup.tests/src/test/resources/methoddispatchresolver => sootup.callgraph/src/test/resources/callgraph}/ConcreteDispatch/D.java (100%) rename {sootup.tests/src/test/resources/methoddispatchresolver => sootup.callgraph/src/test/resources/callgraph}/ConcreteDispatch/I.java (100%) rename {sootup.tests/src/test/resources/methoddispatchresolver => sootup.callgraph/src/test/resources/callgraph}/ConcreteDispatch/J.java (100%) delete mode 100644 sootup.tests/src/test/java/sootup/tests/typehierarchy/MethodDispatchBase.java delete mode 100644 sootup.tests/src/test/java/sootup/tests/typehierarchy/MethodDispatchResolverTest.java delete mode 100644 sootup.tests/src/test/java/sootup/tests/typehierarchy/methoddispatchtestcase/AbstractDispatchTest.java delete mode 100644 sootup.tests/src/test/java/sootup/tests/typehierarchy/methoddispatchtestcase/AllDispatchTest.java delete mode 100644 sootup.tests/src/test/java/sootup/tests/typehierarchy/methoddispatchtestcase/ConcreteDispatchTest.java delete mode 100644 sootup.tests/src/test/java/sootup/tests/typehierarchy/methoddispatchtestcase/CovarianceDispatchTest.java delete mode 100644 sootup.tests/src/test/resources/methoddispatchresolver/AbstractDispatch/A.java delete mode 100644 sootup.tests/src/test/resources/methoddispatchresolver/AbstractDispatch/AbstractClass.java delete mode 100644 sootup.tests/src/test/resources/methoddispatchresolver/AbstractDispatch/B.java delete mode 100644 sootup.tests/src/test/resources/methoddispatchresolver/AbstractDispatch/C.java delete mode 100644 sootup.tests/src/test/resources/methoddispatchresolver/AllDispatch/A.java delete mode 100644 sootup.tests/src/test/resources/methoddispatchresolver/AllDispatch/AbstractClass.java delete mode 100644 sootup.tests/src/test/resources/methoddispatchresolver/AllDispatch/B.java delete mode 100644 sootup.tests/src/test/resources/methoddispatchresolver/AllDispatch/C.java delete mode 100644 sootup.tests/src/test/resources/methoddispatchresolver/CovarianceDispatch/Subclass.java delete mode 100644 sootup.tests/src/test/resources/methoddispatchresolver/CovarianceDispatch/Superclass.java 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 036b4dade8e..a7678c05681 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 @@ -2,6 +2,7 @@ import java.util.*; import java.util.stream.Collectors; +import sootup.callgraph.AbstractCallGraphAlgorithm; import sootup.core.graph.BasicBlock; import sootup.core.graph.StmtGraph; import sootup.core.jimple.common.expr.JNewExpr; @@ -90,7 +91,7 @@ public static Set getMethodSignatureInSubClass( return MethodDispatchResolver.resolveAllDispatches(view, targetMethodSignature).stream() .map( methodSignature -> - MethodDispatchResolver.resolveConcreteDispatch(view, methodSignature)) + AbstractCallGraphAlgorithm.resolveConcreteDispatch(view, methodSignature)) .filter(Optional::isPresent) .map(Optional::get) .collect(Collectors.toSet()); diff --git a/sootup.callgraph/src/main/java/sootup/callgraph/AbstractCallGraphAlgorithm.java b/sootup.callgraph/src/main/java/sootup/callgraph/AbstractCallGraphAlgorithm.java index cf8877e083f..f883f283441 100644 --- a/sootup.callgraph/src/main/java/sootup/callgraph/AbstractCallGraphAlgorithm.java +++ b/sootup.callgraph/src/main/java/sootup/callgraph/AbstractCallGraphAlgorithm.java @@ -28,6 +28,7 @@ import javax.annotation.Nonnull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import sootup.core.IdentifierFactory; import sootup.core.jimple.basic.Value; import sootup.core.jimple.common.expr.AbstractInvokeExpr; import sootup.core.jimple.common.expr.JStaticInvokeExpr; @@ -40,6 +41,8 @@ import sootup.core.model.SootMethod; import sootup.core.signatures.MethodSignature; import sootup.core.signatures.MethodSubSignature; +import sootup.core.typehierarchy.HierarchyComparator; +import sootup.core.typehierarchy.TypeHierarchy; import sootup.core.types.ClassType; import sootup.core.views.View; import sootup.java.core.JavaIdentifierFactory; @@ -458,4 +461,77 @@ public MethodSignature findMainMethod() { @Nonnull protected abstract Stream resolveCall( SootMethod method, AbstractInvokeExpr invokeExpr); + + /** + * Searches for the signature of the method that is the concrete implementation of m. + * This is done by checking each superclass and the class itself for whether it contains the + * concrete implementation. + */ + @Nonnull + public static Optional resolveConcreteDispatch( + View> view, MethodSignature m) { + Optional methodOp = findConcreteMethod(view, m); + if (methodOp.isPresent()) { + SootMethod method = methodOp.get(); + if (method.isAbstract()) { + return Optional.empty(); + } + return Optional.of(method.getSignature()); + } + return Optional.empty(); + } + + /** + * searches the method object in the given hierarchy + * + * @param view it contains all classes + * @param sig the signature of the searched method + * @return the found method object, or null if the method was not found. + */ + public static Optional findConcreteMethod( + @Nonnull View> view, @Nonnull MethodSignature sig) { + IdentifierFactory identifierFactory = view.getIdentifierFactory(); + Optional startMethod = view.getMethod(sig); + if (startMethod.isPresent()) { + return startMethod; + } + TypeHierarchy typeHierarchy = view.getTypeHierarchy(); + + List superClasses = typeHierarchy.superClassesOf(sig.getDeclClassType()); + for (ClassType superClassType : superClasses) { + Optional method = + view.getMethod( + identifierFactory.getMethodSignature(superClassType, sig.getSubSignature())); + if (method.isPresent()) { + return method; + } + } + Set interfaces = typeHierarchy.implementedInterfacesOf(sig.getDeclClassType()); + // interface1 is a sub-interface of interface2 + // interface1 is a super-interface of interface2 + // due to multiple inheritance in interfaces + final HierarchyComparator hierarchyComparator = new HierarchyComparator(view); + Optional defaultMethod = + interfaces.stream() + .map( + classType -> + view.getMethod( + identifierFactory.getMethodSignature(classType, sig.getSubSignature()))) + .filter(Optional::isPresent) + .map(Optional::get) + .min( + (m1, m2) -> + hierarchyComparator.compare( + m1.getDeclaringClassType(), m2.getDeclaringClassType())); + if (defaultMethod.isPresent()) { + return defaultMethod; + } + logger.warn( + "Could not find \"" + + sig.getSubSignature() + + "\" in " + + sig.getDeclClassType().getClassName() + + " and in its superclasses and interfaces"); + return Optional.empty(); + } } diff --git a/sootup.callgraph/src/main/java/sootup/callgraph/ClassHierarchyAnalysisAlgorithm.java b/sootup.callgraph/src/main/java/sootup/callgraph/ClassHierarchyAnalysisAlgorithm.java index b9404432d97..0edee652ea1 100644 --- a/sootup.callgraph/src/main/java/sootup/callgraph/ClassHierarchyAnalysisAlgorithm.java +++ b/sootup.callgraph/src/main/java/sootup/callgraph/ClassHierarchyAnalysisAlgorithm.java @@ -84,8 +84,7 @@ protected Stream resolveCall(SootMethod method, AbstractInvokeE return Stream.empty(); } - SootMethod targetMethod = - MethodDispatchResolver.findConcreteMethod(view, targetMethodSignature).orElse(null); + SootMethod targetMethod =findConcreteMethod(view, targetMethodSignature).orElse(null); if (targetMethod == null || MethodModifier.isStatic(targetMethod.getModifiers()) @@ -103,7 +102,7 @@ protected Stream resolveCall(SootMethod method, AbstractInvokeE noImplementedMethod.stream() .map( classType -> - MethodDispatchResolver.resolveConcreteDispatch( + resolveConcreteDispatch( view, factory.getMethodSignature( classType, targetMethodSignature.getSubSignature()))) diff --git a/sootup.callgraph/src/main/java/sootup/callgraph/RapidTypeAnalysisAlgorithm.java b/sootup.callgraph/src/main/java/sootup/callgraph/RapidTypeAnalysisAlgorithm.java index d92abca3183..1e1d70db9e3 100644 --- a/sootup.callgraph/src/main/java/sootup/callgraph/RapidTypeAnalysisAlgorithm.java +++ b/sootup.callgraph/src/main/java/sootup/callgraph/RapidTypeAnalysisAlgorithm.java @@ -174,15 +174,14 @@ protected Stream resolveCall(SootMethod method, AbstractInvokeE Set concreteCallTargets = implAndOverrides.stream() .map( - methodSignature -> - MethodDispatchResolver.resolveConcreteDispatch(view, methodSignature)) + methodSignature ->resolveConcreteDispatch(view, methodSignature)) .filter(Optional::isPresent) .map(Optional::get) .collect(Collectors.toSet()); // add the concrete of the targetMethod if the class is instantiated if (targetMethodClassIsInstantiated) { - MethodDispatchResolver.resolveConcreteDispatch(view, targetMethodSignature) + resolveConcreteDispatch(view, targetMethodSignature) .ifPresent(concreteCallTargets::add); } @@ -239,8 +238,7 @@ protected void postProcessingMethod( if (newEdges != null) { newEdges.forEach( call -> { - MethodSignature concreteTarget = - MethodDispatchResolver.resolveConcreteDispatch(view, call.target) + MethodSignature concreteTarget =resolveConcreteDispatch(view, call.target) .orElse(null); if (concreteTarget == null) { return; diff --git a/sootup.callgraph/src/test/java/sootup/callgraph/ConcreteDispatchTest.java b/sootup.callgraph/src/test/java/sootup/callgraph/ConcreteDispatchTest.java new file mode 100644 index 00000000000..8da37e2347c --- /dev/null +++ b/sootup.callgraph/src/test/java/sootup/callgraph/ConcreteDispatchTest.java @@ -0,0 +1,188 @@ +package sootup.callgraph; + +import static junit.framework.TestCase.assertEquals; +import static junit.framework.TestCase.assertNotNull; +import static org.junit.Assert.assertFalse; + +import categories.Java8Test; +import java.util.Collections; +import java.util.Optional; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import sootup.core.IdentifierFactory; +import sootup.core.signatures.MethodSignature; +import sootup.core.types.ClassType; +import sootup.java.bytecode.inputlocation.JavaClassPathAnalysisInputLocation; +import sootup.java.core.JavaProject; +import sootup.java.core.language.JavaLanguage; +import sootup.java.core.views.JavaView; +import sootup.java.sourcecode.inputlocation.JavaSourcePathAnalysisInputLocation; + + +/** @author : Hasitha Rajapakse, Jonas Klauke * */ +@Category(Java8Test.class) +public class ConcreteDispatchTest { + public ClassType getClassType(String className) { + return view.getIdentifierFactory().getClassType(className); + } + private static JavaView view; + + @BeforeClass + public static void setUp() { + JavaProject project = + JavaProject.builder(new JavaLanguage(8)) + .addInputLocation( + new JavaSourcePathAnalysisInputLocation( + Collections.singleton("src/test/resources/callgraph/ConcreteDispatch/"))) + .addInputLocation( + new JavaClassPathAnalysisInputLocation( + System.getProperty("java.home") + "/lib/rt.jar")) + .build(); + view = project.createView(); + } + + @Test + public void invalidResolveConcreteDispatch() { + IdentifierFactory factory = view.getIdentifierFactory(); + Optional ms = + AbstractCallGraphAlgorithm.resolveConcreteDispatch( + view, factory.parseMethodSignature("java.util.Collection#size(): int")); + assertFalse(ms.isPresent()); + } + + @Test() + public void invalidResolveConcreteDispatchOfAbstractMethod() { + IdentifierFactory factory = view.getIdentifierFactory(); + Optional ms = + AbstractCallGraphAlgorithm.resolveConcreteDispatch( + view, + factory.parseMethodSignature("java.util.AbstractList#get(int): java.lang.Object")); + assertFalse(ms.isPresent()); + } + + @Test + public void testResolveOfANotImplementedMethodInAbstractClass() { + IdentifierFactory factory = view.getIdentifierFactory(); + Optional emptySig = + AbstractCallGraphAlgorithm.resolveConcreteDispatch( + view, + factory.parseMethodSignature( + "com.sun.java.util.jar.pack.ConstantPool$LiteralEntry#equals(java.lang.Object): boolean")); + assertFalse(emptySig.isPresent()); + } + + @Test + public void resolveConcreteDispatch() { + IdentifierFactory factory = view.getIdentifierFactory(); + MethodSignature strToStringSig = + factory.parseMethodSignature("java.lang.String#toString(): java.lang.String"); + + MethodSignature concreteMethodSig = + AbstractCallGraphAlgorithm.resolveConcreteDispatch(view, strToStringSig).orElse(null); + Assert.assertNotNull(concreteMethodSig); + Assert.assertEquals("String.toString() should resolve to itself", strToStringSig, concreteMethodSig); + + MethodSignature concreteMethodSig2 = + AbstractCallGraphAlgorithm.resolveConcreteDispatch( + view, factory.parseMethodSignature("A#hashCode(): int")) + .orElse(null); + Assert.assertNotNull(concreteMethodSig2); + Assert.assertEquals( + "A.hashCode() should resolve to java.lang.Object.hashCode()", + factory.parseMethodSignature("java.lang.Object#hashCode(): int"), + concreteMethodSig2); + } + @Test + public void method() { + // test concrete method in super class + ClassType sootClassTypeA = getClassType("A"); + ClassType sootClassTypeB = getClassType("B"); + + IdentifierFactory identifierFactory=view.getIdentifierFactory(); + MethodSignature sootMethod1 = + identifierFactory.getMethodSignature( + sootClassTypeA, "method", "void", Collections.emptyList()); + MethodSignature sootMethod2 = + identifierFactory.getMethodSignature( + sootClassTypeA, "method2", "void", Collections.emptyList()); + MethodSignature sootMethod3 = + identifierFactory.getMethodSignature( + sootClassTypeB, "method2", "void", Collections.emptyList()); + + MethodSignature candidate1 = + AbstractCallGraphAlgorithm.resolveConcreteDispatch(view, sootMethod1) + .orElse(null); + assertNotNull(candidate1); + assertEquals(candidate1, sootMethod1); + + MethodSignature candidate2 = + AbstractCallGraphAlgorithm.resolveConcreteDispatch(view, sootMethod2) + .orElse(null); + assertNotNull(candidate2); + assertEquals(candidate2, sootMethod3); + + // test concrete method in interface + ClassType sootClassInterfaceI = getClassType("I"); + MethodSignature sootInterfaceMethod = + identifierFactory.getMethodSignature( + sootClassInterfaceI, "interfaceMethod", "void", Collections.emptyList()); + + MethodSignature sootInterfaceMethodA = + identifierFactory.getMethodSignature( + sootClassTypeA, "interfaceMethod", "void", Collections.emptyList()); + + MethodSignature candidateInterface = + AbstractCallGraphAlgorithm.resolveConcreteDispatch( + view, sootInterfaceMethodA) + .orElse(null); + assertNotNull(candidateInterface); + assertEquals(candidateInterface, sootInterfaceMethod); + + // test concrete method in super-interface + ClassType sootClassInterfaceJ = getClassType("J"); + MethodSignature sootSuperInterfaceMethod = + identifierFactory.getMethodSignature( + sootClassInterfaceJ, "superInterfaceMethod", "void", Collections.emptyList()); + + MethodSignature sootSuperInterfaceMethodA = + identifierFactory.getMethodSignature( + sootClassTypeA, "superInterfaceMethod", "void", Collections.emptyList()); + + MethodSignature candidateSuperInterface = + AbstractCallGraphAlgorithm.resolveConcreteDispatch( + view, sootSuperInterfaceMethodA) + .orElse(null); + assertNotNull(candidateSuperInterface); + assertEquals(candidateSuperInterface, sootSuperInterfaceMethod); + + // test concrete method with two possible default methods but one is in the sub-interface + ClassType sootClassTypeC = getClassType("C"); + ClassType sootClassTypeD = getClassType("D"); + MethodSignature subInterfaceMethod = + identifierFactory.getMethodSignature( + sootClassInterfaceI, "interfaceMethod", "void", Collections.emptyList()); + + MethodSignature sootSuperInterfaceMethodD = + identifierFactory.getMethodSignature( + sootClassTypeD, "interfaceMethod", "void", Collections.emptyList()); + + MethodSignature sootSuperInterfaceMethodC = + identifierFactory.getMethodSignature( + sootClassTypeC, "interfaceMethod", "void", Collections.emptyList()); + + MethodSignature candidateSubInterface = + AbstractCallGraphAlgorithm.resolveConcreteDispatch( + view, sootSuperInterfaceMethodD) + .orElse(null); + assertNotNull(candidateSubInterface); + assertEquals(candidateSubInterface, subInterfaceMethod); + MethodSignature candidateSubInterface2 = + AbstractCallGraphAlgorithm.resolveConcreteDispatch( + view, sootSuperInterfaceMethodC) + .orElse(null); + assertNotNull(candidateSubInterface2); + assertEquals(candidateSubInterface, candidateSubInterface2); + } +} diff --git a/sootup.tests/src/test/resources/methoddispatchresolver/ConcreteDispatch/A.java b/sootup.callgraph/src/test/resources/callgraph/ConcreteDispatch/A.java similarity index 100% rename from sootup.tests/src/test/resources/methoddispatchresolver/ConcreteDispatch/A.java rename to sootup.callgraph/src/test/resources/callgraph/ConcreteDispatch/A.java diff --git a/sootup.tests/src/test/resources/methoddispatchresolver/ConcreteDispatch/B.java b/sootup.callgraph/src/test/resources/callgraph/ConcreteDispatch/B.java similarity index 100% rename from sootup.tests/src/test/resources/methoddispatchresolver/ConcreteDispatch/B.java rename to sootup.callgraph/src/test/resources/callgraph/ConcreteDispatch/B.java diff --git a/sootup.tests/src/test/resources/methoddispatchresolver/ConcreteDispatch/C.java b/sootup.callgraph/src/test/resources/callgraph/ConcreteDispatch/C.java similarity index 100% rename from sootup.tests/src/test/resources/methoddispatchresolver/ConcreteDispatch/C.java rename to sootup.callgraph/src/test/resources/callgraph/ConcreteDispatch/C.java diff --git a/sootup.tests/src/test/resources/methoddispatchresolver/ConcreteDispatch/D.java b/sootup.callgraph/src/test/resources/callgraph/ConcreteDispatch/D.java similarity index 100% rename from sootup.tests/src/test/resources/methoddispatchresolver/ConcreteDispatch/D.java rename to sootup.callgraph/src/test/resources/callgraph/ConcreteDispatch/D.java diff --git a/sootup.tests/src/test/resources/methoddispatchresolver/ConcreteDispatch/I.java b/sootup.callgraph/src/test/resources/callgraph/ConcreteDispatch/I.java similarity index 100% rename from sootup.tests/src/test/resources/methoddispatchresolver/ConcreteDispatch/I.java rename to sootup.callgraph/src/test/resources/callgraph/ConcreteDispatch/I.java diff --git a/sootup.tests/src/test/resources/methoddispatchresolver/ConcreteDispatch/J.java b/sootup.callgraph/src/test/resources/callgraph/ConcreteDispatch/J.java similarity index 100% rename from sootup.tests/src/test/resources/methoddispatchresolver/ConcreteDispatch/J.java rename to sootup.callgraph/src/test/resources/callgraph/ConcreteDispatch/J.java diff --git a/sootup.core/src/main/java/sootup/core/typehierarchy/MethodDispatchResolver.java b/sootup.core/src/main/java/sootup/core/typehierarchy/MethodDispatchResolver.java index 99564b545fb..5c638a5f8bb 100644 --- a/sootup.core/src/main/java/sootup/core/typehierarchy/MethodDispatchResolver.java +++ b/sootup.core/src/main/java/sootup/core/typehierarchy/MethodDispatchResolver.java @@ -42,8 +42,6 @@ public final class MethodDispatchResolver { - private static final Logger logger = LoggerFactory.getLogger(MethodDispatchResolver.class); - private MethodDispatchResolver() {} /** @@ -73,51 +71,6 @@ public static Set resolveAllDispatches( .collect(Collectors.toSet()); } - /** - * Searches the view for classes that implement or override the method m and returns - * the set of method signatures that a method call could resolve to. - */ - @Nonnull - public static Stream resolveAbstractDispatch( - View> view, MethodSignature m) { - TypeHierarchy hierarchy = view.getTypeHierarchy(); - - return hierarchy.subtypesOf(m.getDeclClassType()).stream() - .map( - sootClass -> - view.getMethod( - view.getIdentifierFactory().getMethodSignature(sootClass, m.getSubSignature()))) - .filter(Optional::isPresent) - .map(Optional::get) - .filter(method -> !method.isAbstract()) - .map(Method::getSignature); - } - - /** - * Searches the view for classes that implement or override the method m and returns - * the set of method signatures that a method call could resolve to within the given classes. - */ - @Nonnull - public static Set resolveAllDispatchesInClasses( - View> view, MethodSignature m, Set classes) { - TypeHierarchy hierarchy = view.getTypeHierarchy(); - - return hierarchy.subtypesOf(m.getDeclClassType()).stream() - .map( - subtype -> - view.getClass(subtype) - .orElseThrow( - () -> - new ResolveException( - "Could not resolve " + subtype + ", but found it in hierarchy."))) - .filter(c -> classes.contains(c.getType())) - .map(sootClass -> sootClass.getMethod(m.getSubSignature())) - .filter(Optional::isPresent) - .map(Optional::get) - .filter(method -> !method.isAbstract()) - .map(Method::getSignature) - .collect(Collectors.toSet()); - } /** * Resolves all dispatches of a given call filtered by a set of given classes @@ -154,123 +107,4 @@ public static Set resolveAllDispatchesInClasses( return signatureInClasses; } - /** - * Warning! Assumes that for an abstract dispatch, potentialTarget is declared - * in the same or a subtype of the declaring class of called. - * - *

For a concrete dispatch, assumes that potentialTarget is declared in the same - * or a supertype of the declaring class of called. - * - * @return Whether name and parameters are equal and the return type of potentialTarget - * is compatible with the return type of called. - */ - public static boolean canDispatch( - MethodSignature called, MethodSignature potentialTarget, TypeHierarchy hierarchy) { - return called.getName().equals(potentialTarget.getName()) - && called.getParameterTypes().equals(potentialTarget.getParameterTypes()) - && (called.getType().equals(potentialTarget.getType()) // return types are equal - || hierarchy.isSubtype(called.getType(), potentialTarget.getType())); // covariant - } - - /** - * Searches for the signature of the method that is the concrete implementation of m. - * This is done by checking each superclass and the class itself for whether it contains the - * concrete implementation. - */ - @Nonnull - public static Optional resolveConcreteDispatch( - View> view, MethodSignature m) { - Optional methodOp = findConcreteMethod(view, m); - if (methodOp.isPresent()) { - SootMethod method = methodOp.get(); - if (method.isAbstract()) { - return Optional.empty(); - } - return Optional.of(method.getSignature()); - } - return Optional.empty(); - } - - /** - * searches the method object in the given hierarchy - * - * @param view it contains all classes - * @param sig the signature of the searched method - * @return the found method object, or null if the method was not found. - */ - public static Optional findConcreteMethod( - @Nonnull View> view, @Nonnull MethodSignature sig) { - IdentifierFactory identifierFactory = view.getIdentifierFactory(); - Optional startMethod = view.getMethod(sig); - if (startMethod.isPresent()) { - return startMethod; - } - TypeHierarchy typeHierarchy = view.getTypeHierarchy(); - - List superClasses = typeHierarchy.superClassesOf(sig.getDeclClassType()); - for (ClassType superClassType : superClasses) { - Optional method = - view.getMethod( - identifierFactory.getMethodSignature(superClassType, sig.getSubSignature())); - if (method.isPresent()) { - return method; - } - } - Set interfaces = typeHierarchy.implementedInterfacesOf(sig.getDeclClassType()); - // interface1 is a sub-interface of interface2 - // interface1 is a super-interface of interface2 - // due to multiple inheritance in interfaces - final HierarchyComparator hierarchyComparator = new HierarchyComparator(view); - Optional defaultMethod = - interfaces.stream() - .map( - classType -> - view.getMethod( - identifierFactory.getMethodSignature(classType, sig.getSubSignature()))) - .filter(Optional::isPresent) - .map(Optional::get) - .min( - (m1, m2) -> - hierarchyComparator.compare( - m1.getDeclaringClassType(), m2.getDeclaringClassType())); - if (defaultMethod.isPresent()) { - return defaultMethod; - } - logger.warn( - "Could not find \"" - + sig.getSubSignature() - + "\" in " - + sig.getDeclClassType().getClassName() - + " and in its superclasses and interfaces"); - return Optional.empty(); - } - - /** - * Resolves the actual method called by the specialInvokeExpr that is contained by - * container. - */ - public static MethodSignature resolveSpecialDispatch( - View> view, - JSpecialInvokeExpr specialInvokeExpr, - MethodSignature container) { - MethodSignature specialMethodSig = specialInvokeExpr.getMethodSignature(); - if (specialMethodSig.getSubSignature().getName().equals("")) { - return specialMethodSig; - } - - SootMethod specialMethod = - view.getClass(specialMethodSig.getDeclClassType()) - .flatMap(cl -> cl.getMethod(specialMethodSig.getSubSignature())) - .orElse(null); - if (specialMethod != null && specialMethod.isPrivate()) { - return specialMethodSig; - } - - if (view.getTypeHierarchy() - .isSubtype(container.getDeclClassType(), specialMethodSig.getDeclClassType())) { - return resolveConcreteDispatch(view, specialMethodSig).orElse(null); - } - - return specialMethodSig; - } } diff --git a/sootup.tests/src/test/java/sootup/tests/typehierarchy/MethodDispatchBase.java b/sootup.tests/src/test/java/sootup/tests/typehierarchy/MethodDispatchBase.java deleted file mode 100644 index 2cab39ad2dc..00000000000 --- a/sootup.tests/src/test/java/sootup/tests/typehierarchy/MethodDispatchBase.java +++ /dev/null @@ -1,78 +0,0 @@ -package sootup.tests.typehierarchy; - -import categories.Java8Test; -import java.util.Collections; -import org.junit.ClassRule; -import org.junit.experimental.categories.Category; -import org.junit.rules.TestWatcher; -import org.junit.runner.Description; -import sootup.java.bytecode.inputlocation.JavaClassPathAnalysisInputLocation; -import sootup.java.core.JavaIdentifierFactory; -import sootup.java.core.JavaProject; -import sootup.java.core.language.JavaLanguage; -import sootup.java.core.types.JavaClassType; -import sootup.java.core.views.JavaView; -import sootup.java.sourcecode.inputlocation.JavaSourcePathAnalysisInputLocation; - -/** @author Hasitha Rajapakse */ -@Category(Java8Test.class) -public class MethodDispatchBase { - static final String baseDir = "src/test/resources/methoddispatchresolver/"; - protected JavaIdentifierFactory identifierFactory = JavaIdentifierFactory.getInstance(); - - @ClassRule - public static MethodDispatchBase.CustomTestWatcher customTestWatcher = - new MethodDispatchBase.CustomTestWatcher(); - - public static class CustomTestWatcher extends TestWatcher { - private String className = MethodDispatchBase.class.getSimpleName(); - private JavaView view; - - @Override - protected void starting(Description description) { - String prevClassName = getClassName(); - setClassName(extractClassName(description.getClassName())); - if (!prevClassName.equals(getClassName())) { - JavaProject project = - JavaProject.builder(new JavaLanguage(8)) - .addInputLocation( - new JavaSourcePathAnalysisInputLocation( - Collections.singleton(baseDir + "/" + getClassName()))) - .addInputLocation( - new JavaClassPathAnalysisInputLocation( - System.getProperty("java.home") + "/lib/rt.jar")) - .build(); - setView(project.createView()); - } - } - - public String getClassName() { - return className; - } - - private void setClassName(String className) { - this.className = className; - } - - private void setView(JavaView view) { - this.view = view; - } - - public JavaView getView() { - return view; - } - } - - public JavaClassType getClassType(String className) { - return identifierFactory.getClassType(className); - } - - public static String extractClassName(String classPath) { - String classPathArray = classPath.substring(classPath.lastIndexOf(".") + 1); - String testDirectoryName = ""; - if (!classPathArray.isEmpty()) { - testDirectoryName = classPathArray.substring(0, classPathArray.length() - 4); - } - return testDirectoryName; - } -} diff --git a/sootup.tests/src/test/java/sootup/tests/typehierarchy/MethodDispatchResolverTest.java b/sootup.tests/src/test/java/sootup/tests/typehierarchy/MethodDispatchResolverTest.java deleted file mode 100644 index 4c7737c13e6..00000000000 --- a/sootup.tests/src/test/java/sootup/tests/typehierarchy/MethodDispatchResolverTest.java +++ /dev/null @@ -1,235 +0,0 @@ -package sootup.tests.typehierarchy; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -import categories.Java8Test; -import java.io.File; -import java.lang.management.ManagementFactory; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; -import org.junit.Before; -import org.junit.Test; -import org.junit.experimental.categories.Category; -import sootup.core.IdentifierFactory; -import sootup.core.jimple.basic.Local; -import sootup.core.jimple.common.expr.JSpecialInvokeExpr; -import sootup.core.signatures.MethodSignature; -import sootup.core.typehierarchy.MethodDispatchResolver; -import sootup.core.types.ClassType; -import sootup.core.util.ImmutableUtils; -import sootup.java.bytecode.inputlocation.JavaClassPathAnalysisInputLocation; -import sootup.java.core.JavaProject; -import sootup.java.core.language.JavaJimple; -import sootup.java.core.language.JavaLanguage; -import sootup.java.core.views.JavaView; - -/** @author Kaustubh Kelkar update on 22.04.2020 */ -@Category(Java8Test.class) -public class MethodDispatchResolverTest { - - private JavaView view; - public static final String jarFile = "../shared-test-resources/java-miniapps/MiniApp.jar"; - - @Before - public void setUp() { - assertTrue("File " + jarFile + " not found.", new File(jarFile).exists()); - String currentClassPath = - System.getProperty("java.class.path") - + File.pathSeparator - + ManagementFactory.getRuntimeMXBean().getBootClassPath(); - String rtJarClassPath = - Arrays.stream(currentClassPath.split(File.pathSeparator)) - .filter(pathEntry -> pathEntry.endsWith(File.separator + "rt.jar")) - .distinct() - .collect(Collectors.joining(File.pathSeparator)); - JavaClassPathAnalysisInputLocation analysisInputLocation = - new JavaClassPathAnalysisInputLocation(jarFile + File.pathSeparator + rtJarClassPath); - JavaProject p = - JavaProject.builder(new JavaLanguage(8)).addInputLocation(analysisInputLocation).build(); - view = p.createView(); - } - - @Test - public void resolveAbstractDispatch() { - IdentifierFactory factory = view.getIdentifierFactory(); - MethodSignature collectionSize = - factory.parseMethodSignature("java.util.Collection#size(): int"); - MethodSignature abstractListSize = - factory.parseMethodSignature("java.util.AbstractList#size(): int"); - MethodSignature setSize = factory.parseMethodSignature("java.util.HashSet#size(): int"); - MethodSignature listSize = factory.parseMethodSignature("java.util.ArrayList#size(): int"); - MethodSignature enumSetClone = - factory.parseMethodSignature("java.util.EnumSet#clone(): java.lang.Object"); - MethodSignature objectClone = - factory.parseMethodSignature("java.lang.Object#clone(): java.lang.Object"); - MethodSignature arrayDequeueClone = - factory.parseMethodSignature("java.util.ArrayDeque#clone(): java.util.ArrayDequeue"); - - Set candidates = - MethodDispatchResolver.resolveAbstractDispatch(view, collectionSize) - .collect(Collectors.toSet()); - assertTrue(collectionSize + " can resolve to " + setSize, candidates.contains(setSize)); - assertTrue(collectionSize + " can resolve to " + listSize, candidates.contains(listSize)); - - assertTrue( - abstractListSize + " can resolve to " + listSize, - MethodDispatchResolver.resolveAbstractDispatch(view, abstractListSize) - .collect(Collectors.toSet()) - .contains(listSize)); - - assertTrue( - objectClone + " can resolve to " + enumSetClone, - MethodDispatchResolver.resolveAbstractDispatch(view, objectClone) - .collect(Collectors.toSet()) - .contains(enumSetClone)); - assertFalse( - arrayDequeueClone + " cannot resolve to " + enumSetClone, - MethodDispatchResolver.resolveAbstractDispatch(view, arrayDequeueClone) - .collect(Collectors.toSet()) - .contains(enumSetClone)); - } - - @Test - public void testResolveAllDispatchesInClasses() { - IdentifierFactory factory = view.getIdentifierFactory(); - MethodSignature collectionSize = - factory.parseMethodSignature("java.util.Collection#size(): int"); - MethodSignature abstractListSize = - factory.parseMethodSignature("java.util.AbstractList#size(): int"); - MethodSignature setSize = factory.parseMethodSignature("java.util.HashSet#size(): int"); - MethodSignature listSize = factory.parseMethodSignature("java.util.ArrayList#size(): int"); - - Set classes = new HashSet<>(); - classes.add(factory.getClassType("java.util.HashSet")); - classes.add(factory.getClassType("java.util.ArrayList")); - Set candidates = - MethodDispatchResolver.resolveAllDispatchesInClasses(view, collectionSize, classes); - assertTrue(collectionSize + " can resolve to " + setSize, candidates.contains(setSize)); - assertTrue(collectionSize + " can resolve to " + listSize, candidates.contains(listSize)); - assertFalse( - collectionSize + " can resolve to " + abstractListSize, - candidates.contains(abstractListSize)); - } - - @Test - public void testCanDispatch() { - IdentifierFactory factory = view.getIdentifierFactory(); - MethodSignature collectionSize = - factory.parseMethodSignature("java.util.Collection#size(): int"); - MethodSignature setSize = factory.parseMethodSignature("java.util.HashSet#size(): int"); - assertTrue( - MethodDispatchResolver.canDispatch(collectionSize, setSize, view.getTypeHierarchy())); - - MethodSignature objectClone = - factory.parseMethodSignature("java.lang.Object#clone(): java.lang.Object"); - MethodSignature arrayDequeueClone = - factory.parseMethodSignature("java.util.ArrayDeque#clone(): java.util.ArrayDequeue"); - assertTrue( - MethodDispatchResolver.canDispatch( - objectClone, arrayDequeueClone, view.getTypeHierarchy())); - - MethodSignature collectionHashCode = - factory.parseMethodSignature("java.util.Collection#hasCode(): int"); - assertFalse( - MethodDispatchResolver.canDispatch( - collectionSize, collectionHashCode, view.getTypeHierarchy())); - - MethodSignature objectWait1Param = - factory.parseMethodSignature("java.lang.Object#wait(long): void"); - MethodSignature objectWait2Param = - factory.parseMethodSignature("java.util.Collection#hasCode(long,int): int"); - assertFalse( - MethodDispatchResolver.canDispatch( - objectWait1Param, objectWait2Param, view.getTypeHierarchy())); - } - - @Test - public void invalidResolveConcreteDispatch() { - IdentifierFactory factory = view.getIdentifierFactory(); - Optional ms = - MethodDispatchResolver.resolveConcreteDispatch( - view, factory.parseMethodSignature("java.util.Collection#size(): int")); - assertFalse(ms.isPresent()); - } - - @Test() - public void invalidResolveConcreteDispatchOfAbstractMethod() { - IdentifierFactory factory = view.getIdentifierFactory(); - Optional ms = - MethodDispatchResolver.resolveConcreteDispatch( - view, - factory.parseMethodSignature("java.util.AbstractList#get(int): java.lang.Object")); - assertFalse(ms.isPresent()); - } - - @Test - public void testResolveOfANotImplementedMethodInAbstractClass() { - IdentifierFactory factory = view.getIdentifierFactory(); - Optional emptySig = - MethodDispatchResolver.resolveConcreteDispatch( - view, - factory.parseMethodSignature( - "com.sun.java.util.jar.pack.ConstantPool$LiteralEntry#equals(java.lang.Object): boolean")); - assertFalse(emptySig.isPresent()); - } - - @Test - public void resolveConcreteDispatch() { - IdentifierFactory factory = view.getIdentifierFactory(); - MethodSignature strToStringSig = - factory.parseMethodSignature("java.lang.String#toString(): java.lang.String"); - - MethodSignature concreteMethodSig = - MethodDispatchResolver.resolveConcreteDispatch(view, strToStringSig).orElse(null); - assertNotNull(concreteMethodSig); - assertEquals("String.toString() should resolve to itself", strToStringSig, concreteMethodSig); - - MethodSignature concreteMethodSig2 = - MethodDispatchResolver.resolveConcreteDispatch( - view, factory.parseMethodSignature("ds.AbstractDataStrcture#hashCode(): int")) - .orElse(null); - assertNotNull(concreteMethodSig2); - assertEquals( - "ds.AbstractDataStrcture.hashCode() should resolve to java.lang.Object.hashCode()", - factory.parseMethodSignature("java.lang.Object#hashCode(): int"), - concreteMethodSig2); - } - - @Test - public void resolveSpecialDispatch() { - IdentifierFactory factory = view.getIdentifierFactory(); - MethodSignature strInit = - factory.parseMethodSignature("java.lang.String#(java.lang.String): void"); - MethodSignature strToStringSig = - factory.parseMethodSignature("java.lang.String#toString(): java.lang.String"); - - JSpecialInvokeExpr strInitInvoke = - new JSpecialInvokeExpr( - new Local("str", factory.getClassType("java.lang.String")), - strInit, - ImmutableUtils.immutableList(JavaJimple.getInstance().newStringConstant("abc"))); - - assertEquals( - "String init should resolve to itself", - strInit, - MethodDispatchResolver.resolveSpecialDispatch(view, strInitInvoke, strToStringSig)); - - MethodSignature privateExplode = - factory.parseMethodSignature("ds.Employee#setEmpName(java.lang.String): java.lang.String"); - JSpecialInvokeExpr privateExplodeInvoke = - new JSpecialInvokeExpr( - new Local("jcp", factory.getClassType("ds.Employee")), - privateExplode, - ImmutableUtils.immutableList(JavaJimple.getInstance().newStringConstant("abc"))); - assertEquals( - privateExplode + " is private and should resolve to itself", - privateExplode, - MethodDispatchResolver.resolveSpecialDispatch(view, privateExplodeInvoke, strToStringSig)); - } -} diff --git a/sootup.tests/src/test/java/sootup/tests/typehierarchy/methoddispatchtestcase/AbstractDispatchTest.java b/sootup.tests/src/test/java/sootup/tests/typehierarchy/methoddispatchtestcase/AbstractDispatchTest.java deleted file mode 100644 index bf0260850ef..00000000000 --- a/sootup.tests/src/test/java/sootup/tests/typehierarchy/methoddispatchtestcase/AbstractDispatchTest.java +++ /dev/null @@ -1,62 +0,0 @@ -package sootup.tests.typehierarchy.methoddispatchtestcase; - -import static org.junit.Assert.*; - -import categories.Java8Test; -import java.util.Collections; -import java.util.Set; -import java.util.stream.Collectors; -import org.junit.Test; -import org.junit.experimental.categories.Category; -import sootup.core.signatures.MethodSignature; -import sootup.core.typehierarchy.MethodDispatchResolver; -import sootup.core.types.ClassType; -import sootup.tests.typehierarchy.MethodDispatchBase; - -/** @author : Hasitha Rajapakse * */ -@Category(Java8Test.class) -public class AbstractDispatchTest extends MethodDispatchBase { - @Test - public void method() { - - ClassType sootClassTypeA = getClassType("A"); - ClassType sootClassTypeB = getClassType("B"); - ClassType sootClassTypeC = getClassType("C"); - ClassType sootClassTypeAbstract = getClassType("AbstractClass"); - - MethodSignature sootMethodA = - identifierFactory.getMethodSignature( - sootClassTypeA, "method", "void", Collections.emptyList()); - MethodSignature sootMethodB = - identifierFactory.getMethodSignature( - sootClassTypeB, "method", "void", Collections.emptyList()); - MethodSignature sootMethodC = - identifierFactory.getMethodSignature( - sootClassTypeC, "method", "void", Collections.emptyList()); - MethodSignature sootMethodAbstract = - identifierFactory.getMethodSignature( - sootClassTypeAbstract, "method", "void", Collections.emptyList()); - - Set candidatesAbstract = - MethodDispatchResolver.resolveAbstractDispatch( - customTestWatcher.getView(), - identifierFactory.getMethodSignature( - sootClassTypeA, "method", "void", Collections.emptyList())) - .collect(Collectors.toSet()); - assertFalse(candidatesAbstract.contains(sootMethodAbstract)); - assertFalse(candidatesAbstract.contains(sootMethodA)); - assertFalse(candidatesAbstract.contains(sootMethodB)); - assertFalse(candidatesAbstract.contains(sootMethodC)); - - Set candidatesSuper = - MethodDispatchResolver.resolveAbstractDispatch( - customTestWatcher.getView(), - identifierFactory.getMethodSignature( - sootClassTypeAbstract, "method", "void", Collections.emptyList())) - .collect(Collectors.toSet()); - assertFalse(candidatesSuper.contains(sootMethodAbstract)); - assertFalse(candidatesSuper.contains(sootMethodA)); - assertFalse(candidatesSuper.contains(sootMethodB)); - assertTrue(candidatesSuper.contains(sootMethodC)); - } -} diff --git a/sootup.tests/src/test/java/sootup/tests/typehierarchy/methoddispatchtestcase/AllDispatchTest.java b/sootup.tests/src/test/java/sootup/tests/typehierarchy/methoddispatchtestcase/AllDispatchTest.java deleted file mode 100644 index 93ed01a6156..00000000000 --- a/sootup.tests/src/test/java/sootup/tests/typehierarchy/methoddispatchtestcase/AllDispatchTest.java +++ /dev/null @@ -1,46 +0,0 @@ -package sootup.tests.typehierarchy.methoddispatchtestcase; - -import static org.junit.Assert.*; - -import categories.Java8Test; -import java.util.Collections; -import java.util.Set; -import org.junit.Test; -import org.junit.experimental.categories.Category; -import sootup.core.signatures.MethodSignature; -import sootup.core.typehierarchy.MethodDispatchResolver; -import sootup.core.types.ClassType; -import sootup.tests.typehierarchy.MethodDispatchBase; - -/** @author : Jonas Klauke * */ -@Category(Java8Test.class) -public class AllDispatchTest extends MethodDispatchBase { - @Test - public void method() { - - ClassType sootClassTypeA = getClassType("A"); - ClassType sootClassTypeB = getClassType("B"); - ClassType sootClassTypeC = getClassType("C"); - ClassType sootClassTypeAbstract = getClassType("AbstractClass"); - - MethodSignature sootMethodB = - identifierFactory.getMethodSignature( - sootClassTypeB, "method", "void", Collections.emptyList()); - MethodSignature sootMethodC = - identifierFactory.getMethodSignature( - sootClassTypeC, "method", "void", Collections.emptyList()); - MethodSignature sootMethodAbstract = - identifierFactory.getMethodSignature( - sootClassTypeAbstract, "method", "void", Collections.emptyList()); - - Set candidatesAbstract = - MethodDispatchResolver.resolveAllDispatches( - customTestWatcher.getView(), - identifierFactory.getMethodSignature( - sootClassTypeA, "method", "void", Collections.emptyList())); - - assertTrue(candidatesAbstract.contains(sootMethodB)); - assertTrue(candidatesAbstract.contains(sootMethodC)); - assertFalse(candidatesAbstract.contains(sootMethodAbstract)); - } -} diff --git a/sootup.tests/src/test/java/sootup/tests/typehierarchy/methoddispatchtestcase/ConcreteDispatchTest.java b/sootup.tests/src/test/java/sootup/tests/typehierarchy/methoddispatchtestcase/ConcreteDispatchTest.java deleted file mode 100644 index 2db176d856e..00000000000 --- a/sootup.tests/src/test/java/sootup/tests/typehierarchy/methoddispatchtestcase/ConcreteDispatchTest.java +++ /dev/null @@ -1,107 +0,0 @@ -package sootup.tests.typehierarchy.methoddispatchtestcase; - -import static junit.framework.TestCase.*; - -import categories.Java8Test; -import java.util.Collections; -import org.junit.Test; -import org.junit.experimental.categories.Category; -import sootup.core.signatures.MethodSignature; -import sootup.core.typehierarchy.MethodDispatchResolver; -import sootup.core.types.ClassType; -import sootup.tests.typehierarchy.MethodDispatchBase; - -/** @author : Hasitha Rajapakse, Jonas Klauke * */ -@Category(Java8Test.class) -public class ConcreteDispatchTest extends MethodDispatchBase { - @Test - public void method() { - // test concrete method in super class - ClassType sootClassTypeA = getClassType("A"); - ClassType sootClassTypeB = getClassType("B"); - - MethodSignature sootMethod1 = - identifierFactory.getMethodSignature( - sootClassTypeA, "method", "void", Collections.emptyList()); - MethodSignature sootMethod2 = - identifierFactory.getMethodSignature( - sootClassTypeA, "method2", "void", Collections.emptyList()); - MethodSignature sootMethod3 = - identifierFactory.getMethodSignature( - sootClassTypeB, "method2", "void", Collections.emptyList()); - - MethodSignature candidate1 = - MethodDispatchResolver.resolveConcreteDispatch(customTestWatcher.getView(), sootMethod1) - .orElse(null); - assertNotNull(candidate1); - assertEquals(candidate1, sootMethod1); - - MethodSignature candidate2 = - MethodDispatchResolver.resolveConcreteDispatch(customTestWatcher.getView(), sootMethod2) - .orElse(null); - assertNotNull(candidate2); - assertEquals(candidate2, sootMethod3); - - // test concrete method in interface - ClassType sootClassInterfaceI = getClassType("I"); - MethodSignature sootInterfaceMethod = - identifierFactory.getMethodSignature( - sootClassInterfaceI, "interfaceMethod", "void", Collections.emptyList()); - - MethodSignature sootInterfaceMethodA = - identifierFactory.getMethodSignature( - sootClassTypeA, "interfaceMethod", "void", Collections.emptyList()); - - MethodSignature candidateInterface = - MethodDispatchResolver.resolveConcreteDispatch( - customTestWatcher.getView(), sootInterfaceMethodA) - .orElse(null); - assertNotNull(candidateInterface); - assertEquals(candidateInterface, sootInterfaceMethod); - - // test concrete method in super-interface - ClassType sootClassInterfaceJ = getClassType("J"); - MethodSignature sootSuperInterfaceMethod = - identifierFactory.getMethodSignature( - sootClassInterfaceJ, "superInterfaceMethod", "void", Collections.emptyList()); - - MethodSignature sootSuperInterfaceMethodA = - identifierFactory.getMethodSignature( - sootClassTypeA, "superInterfaceMethod", "void", Collections.emptyList()); - - MethodSignature candidateSuperInterface = - MethodDispatchResolver.resolveConcreteDispatch( - customTestWatcher.getView(), sootSuperInterfaceMethodA) - .orElse(null); - assertNotNull(candidateSuperInterface); - assertEquals(candidateSuperInterface, sootSuperInterfaceMethod); - - // test concrete method with two possible default methods but one is in the sub-interface - ClassType sootClassTypeC = getClassType("C"); - ClassType sootClassTypeD = getClassType("D"); - MethodSignature subInterfaceMethod = - identifierFactory.getMethodSignature( - sootClassInterfaceI, "interfaceMethod", "void", Collections.emptyList()); - - MethodSignature sootSuperInterfaceMethodD = - identifierFactory.getMethodSignature( - sootClassTypeD, "interfaceMethod", "void", Collections.emptyList()); - - MethodSignature sootSuperInterfaceMethodC = - identifierFactory.getMethodSignature( - sootClassTypeC, "interfaceMethod", "void", Collections.emptyList()); - - MethodSignature candidateSubInterface = - MethodDispatchResolver.resolveConcreteDispatch( - customTestWatcher.getView(), sootSuperInterfaceMethodD) - .orElse(null); - assertNotNull(candidateSubInterface); - assertEquals(candidateSubInterface, subInterfaceMethod); - MethodSignature candidateSubInterface2 = - MethodDispatchResolver.resolveConcreteDispatch( - customTestWatcher.getView(), sootSuperInterfaceMethodC) - .orElse(null); - assertNotNull(candidateSubInterface2); - assertEquals(candidateSubInterface, candidateSubInterface2); - } -} diff --git a/sootup.tests/src/test/java/sootup/tests/typehierarchy/methoddispatchtestcase/CovarianceDispatchTest.java b/sootup.tests/src/test/java/sootup/tests/typehierarchy/methoddispatchtestcase/CovarianceDispatchTest.java deleted file mode 100644 index 4a6c2e090f4..00000000000 --- a/sootup.tests/src/test/java/sootup/tests/typehierarchy/methoddispatchtestcase/CovarianceDispatchTest.java +++ /dev/null @@ -1,45 +0,0 @@ -package sootup.tests.typehierarchy.methoddispatchtestcase; - -import static org.junit.Assert.*; - -import categories.Java8Test; -import java.util.Set; -import org.junit.Test; -import org.junit.experimental.categories.Category; -import sootup.core.signatures.MethodSignature; -import sootup.core.typehierarchy.MethodDispatchResolver; -import sootup.tests.typehierarchy.MethodDispatchBase; - -/** @author : Jonas Klauke * */ -@Category(Java8Test.class) -public class CovarianceDispatchTest extends MethodDispatchBase { - @Test - public void method() { - // sourcecode frontend test - Set sourcecodeCandidates = - MethodDispatchResolver.resolveAllDispatches( - customTestWatcher.getView(), - identifierFactory.parseMethodSignature("Superclass#method(): java.lang.Object")); - - assertFalse( - sourcecodeCandidates.contains( - identifierFactory.parseMethodSignature("Subclass#method(): Subclass"))); - assertTrue( - sourcecodeCandidates.contains( - identifierFactory.parseMethodSignature("Subclass#method(): java.lang.Object"))); - - // bytecode frontend test - Set bytecodeCandidates = - MethodDispatchResolver.resolveAllDispatches( - customTestWatcher.getView(), - identifierFactory.parseMethodSignature( - "java.util.AbstractSet#clone(): java.lang.Object")); - assertFalse( - bytecodeCandidates.contains( - identifierFactory.parseMethodSignature( - "java.util.EnumSet#clone(): java.util.EnumSet"))); - assertTrue( - bytecodeCandidates.contains( - identifierFactory.parseMethodSignature("java.util.EnumSet#clone(): java.lang.Object"))); - } -} diff --git a/sootup.tests/src/test/resources/methoddispatchresolver/AbstractDispatch/A.java b/sootup.tests/src/test/resources/methoddispatchresolver/AbstractDispatch/A.java deleted file mode 100644 index 3210ccd9c79..00000000000 --- a/sootup.tests/src/test/resources/methoddispatchresolver/AbstractDispatch/A.java +++ /dev/null @@ -1,8 +0,0 @@ - -/** @author: Hasitha Rajapakse **/ - -public class A { - public void method(){ - int num = 5; - } -} \ No newline at end of file diff --git a/sootup.tests/src/test/resources/methoddispatchresolver/AbstractDispatch/AbstractClass.java b/sootup.tests/src/test/resources/methoddispatchresolver/AbstractDispatch/AbstractClass.java deleted file mode 100644 index 52e0dc27863..00000000000 --- a/sootup.tests/src/test/resources/methoddispatchresolver/AbstractDispatch/AbstractClass.java +++ /dev/null @@ -1,6 +0,0 @@ - -/** @author: Hasitha Rajapakse **/ - -public abstract class AbstractClass{ - public abstract void method(); -} \ No newline at end of file diff --git a/sootup.tests/src/test/resources/methoddispatchresolver/AbstractDispatch/B.java b/sootup.tests/src/test/resources/methoddispatchresolver/AbstractDispatch/B.java deleted file mode 100644 index 8194752b9d7..00000000000 --- a/sootup.tests/src/test/resources/methoddispatchresolver/AbstractDispatch/B.java +++ /dev/null @@ -1,5 +0,0 @@ - -/** @author: Hasitha Rajapakse **/ - -public class B extends AbstractClass { -} \ No newline at end of file diff --git a/sootup.tests/src/test/resources/methoddispatchresolver/AbstractDispatch/C.java b/sootup.tests/src/test/resources/methoddispatchresolver/AbstractDispatch/C.java deleted file mode 100644 index 9bb6eaeb8e6..00000000000 --- a/sootup.tests/src/test/resources/methoddispatchresolver/AbstractDispatch/C.java +++ /dev/null @@ -1,8 +0,0 @@ - -/** @author: Hasitha Rajapakse **/ - -public class C extends AbstractClass { - public void method(){ - int num = 20; - } -} \ No newline at end of file diff --git a/sootup.tests/src/test/resources/methoddispatchresolver/AllDispatch/A.java b/sootup.tests/src/test/resources/methoddispatchresolver/AllDispatch/A.java deleted file mode 100644 index ee9d7735d8e..00000000000 --- a/sootup.tests/src/test/resources/methoddispatchresolver/AllDispatch/A.java +++ /dev/null @@ -1,7 +0,0 @@ - -/** @author: Jonas Klauke **/ - -public class A { - public void method(){ - } -} \ No newline at end of file diff --git a/sootup.tests/src/test/resources/methoddispatchresolver/AllDispatch/AbstractClass.java b/sootup.tests/src/test/resources/methoddispatchresolver/AllDispatch/AbstractClass.java deleted file mode 100644 index 3de86bb38d1..00000000000 --- a/sootup.tests/src/test/resources/methoddispatchresolver/AllDispatch/AbstractClass.java +++ /dev/null @@ -1,6 +0,0 @@ - -/** @author: Jonas Klauke **/ - -public abstract class AbstractClass extends A{ - public abstract void method(); -} \ No newline at end of file diff --git a/sootup.tests/src/test/resources/methoddispatchresolver/AllDispatch/B.java b/sootup.tests/src/test/resources/methoddispatchresolver/AllDispatch/B.java deleted file mode 100644 index 30144f71c34..00000000000 --- a/sootup.tests/src/test/resources/methoddispatchresolver/AllDispatch/B.java +++ /dev/null @@ -1,5 +0,0 @@ - -/** @author: Jonas Klauke **/ - -public class B extends AbstractClass { -} \ No newline at end of file diff --git a/sootup.tests/src/test/resources/methoddispatchresolver/AllDispatch/C.java b/sootup.tests/src/test/resources/methoddispatchresolver/AllDispatch/C.java deleted file mode 100644 index f482c4f0787..00000000000 --- a/sootup.tests/src/test/resources/methoddispatchresolver/AllDispatch/C.java +++ /dev/null @@ -1,8 +0,0 @@ - -/** @author: Jonas Klauke **/ - -public class C extends A { - public void method(){ - int num = 20; - } -} \ No newline at end of file diff --git a/sootup.tests/src/test/resources/methoddispatchresolver/CovarianceDispatch/Subclass.java b/sootup.tests/src/test/resources/methoddispatchresolver/CovarianceDispatch/Subclass.java deleted file mode 100644 index 138c1993d73..00000000000 --- a/sootup.tests/src/test/resources/methoddispatchresolver/CovarianceDispatch/Subclass.java +++ /dev/null @@ -1,8 +0,0 @@ - -/** @author: Jonas Klauke **/ - -public class Subclass extends Superclass { - public Superclass method(){ - return new Superclass(); - } -} \ No newline at end of file diff --git a/sootup.tests/src/test/resources/methoddispatchresolver/CovarianceDispatch/Superclass.java b/sootup.tests/src/test/resources/methoddispatchresolver/CovarianceDispatch/Superclass.java deleted file mode 100644 index f041d65e412..00000000000 --- a/sootup.tests/src/test/resources/methoddispatchresolver/CovarianceDispatch/Superclass.java +++ /dev/null @@ -1,8 +0,0 @@ - -/** @author: Jonas Klauke **/ - -public class Superclass{ - public Object method(){ - return new Object(); - } -} \ No newline at end of file From c0663702d744d3dafaf365fad81267071740111e Mon Sep 17 00:00:00 2001 From: Jonas Klauke Date: Tue, 14 Nov 2023 14:37:35 +0100 Subject: [PATCH 13/23] fixed broken field access to removed field --- .../ClassHierarchyAnalysisAlgorithm.java | 3 +-- .../callgraph/RapidTypeAnalysisAlgorithm.java | 10 +++---- .../callgraph/ConcreteDispatchTest.java | 27 ++++++++----------- .../typehierarchy/MethodDispatchResolver.java | 11 -------- .../typehierarchy/ViewTypeHierarchyTest.java | 2 +- 5 files changed, 17 insertions(+), 36 deletions(-) diff --git a/sootup.callgraph/src/main/java/sootup/callgraph/ClassHierarchyAnalysisAlgorithm.java b/sootup.callgraph/src/main/java/sootup/callgraph/ClassHierarchyAnalysisAlgorithm.java index 0edee652ea1..f360c852089 100644 --- a/sootup.callgraph/src/main/java/sootup/callgraph/ClassHierarchyAnalysisAlgorithm.java +++ b/sootup.callgraph/src/main/java/sootup/callgraph/ClassHierarchyAnalysisAlgorithm.java @@ -34,7 +34,6 @@ import sootup.core.model.SootClass; import sootup.core.model.SootMethod; import sootup.core.signatures.MethodSignature; -import sootup.core.typehierarchy.MethodDispatchResolver; import sootup.core.types.ClassType; import sootup.core.views.View; @@ -84,7 +83,7 @@ protected Stream resolveCall(SootMethod method, AbstractInvokeE return Stream.empty(); } - SootMethod targetMethod =findConcreteMethod(view, targetMethodSignature).orElse(null); + SootMethod targetMethod = findConcreteMethod(view, targetMethodSignature).orElse(null); if (targetMethod == null || MethodModifier.isStatic(targetMethod.getModifiers()) diff --git a/sootup.callgraph/src/main/java/sootup/callgraph/RapidTypeAnalysisAlgorithm.java b/sootup.callgraph/src/main/java/sootup/callgraph/RapidTypeAnalysisAlgorithm.java index 1e1d70db9e3..95e858ec02f 100644 --- a/sootup.callgraph/src/main/java/sootup/callgraph/RapidTypeAnalysisAlgorithm.java +++ b/sootup.callgraph/src/main/java/sootup/callgraph/RapidTypeAnalysisAlgorithm.java @@ -173,16 +173,14 @@ protected Stream resolveCall(SootMethod method, AbstractInvokeE // find the concrete dispatch of all possible dispatches Set concreteCallTargets = implAndOverrides.stream() - .map( - methodSignature ->resolveConcreteDispatch(view, methodSignature)) + .map(methodSignature -> resolveConcreteDispatch(view, methodSignature)) .filter(Optional::isPresent) .map(Optional::get) .collect(Collectors.toSet()); // add the concrete of the targetMethod if the class is instantiated if (targetMethodClassIsInstantiated) { - resolveConcreteDispatch(view, targetMethodSignature) - .ifPresent(concreteCallTargets::add); + resolveConcreteDispatch(view, targetMethodSignature).ifPresent(concreteCallTargets::add); } return concreteCallTargets.stream(); @@ -238,8 +236,8 @@ protected void postProcessingMethod( if (newEdges != null) { newEdges.forEach( call -> { - MethodSignature concreteTarget =resolveConcreteDispatch(view, call.target) - .orElse(null); + MethodSignature concreteTarget = + resolveConcreteDispatch(view, call.target).orElse(null); if (concreteTarget == null) { return; } diff --git a/sootup.callgraph/src/test/java/sootup/callgraph/ConcreteDispatchTest.java b/sootup.callgraph/src/test/java/sootup/callgraph/ConcreteDispatchTest.java index 8da37e2347c..5bb68192ae1 100644 --- a/sootup.callgraph/src/test/java/sootup/callgraph/ConcreteDispatchTest.java +++ b/sootup.callgraph/src/test/java/sootup/callgraph/ConcreteDispatchTest.java @@ -20,13 +20,13 @@ import sootup.java.core.views.JavaView; import sootup.java.sourcecode.inputlocation.JavaSourcePathAnalysisInputLocation; - /** @author : Hasitha Rajapakse, Jonas Klauke * */ @Category(Java8Test.class) public class ConcreteDispatchTest { public ClassType getClassType(String className) { return view.getIdentifierFactory().getClassType(className); } + private static JavaView view; @BeforeClass @@ -82,7 +82,8 @@ public void resolveConcreteDispatch() { MethodSignature concreteMethodSig = AbstractCallGraphAlgorithm.resolveConcreteDispatch(view, strToStringSig).orElse(null); Assert.assertNotNull(concreteMethodSig); - Assert.assertEquals("String.toString() should resolve to itself", strToStringSig, concreteMethodSig); + Assert.assertEquals( + "String.toString() should resolve to itself", strToStringSig, concreteMethodSig); MethodSignature concreteMethodSig2 = AbstractCallGraphAlgorithm.resolveConcreteDispatch( @@ -94,13 +95,14 @@ public void resolveConcreteDispatch() { factory.parseMethodSignature("java.lang.Object#hashCode(): int"), concreteMethodSig2); } + @Test public void method() { // test concrete method in super class ClassType sootClassTypeA = getClassType("A"); ClassType sootClassTypeB = getClassType("B"); - IdentifierFactory identifierFactory=view.getIdentifierFactory(); + IdentifierFactory identifierFactory = view.getIdentifierFactory(); MethodSignature sootMethod1 = identifierFactory.getMethodSignature( sootClassTypeA, "method", "void", Collections.emptyList()); @@ -112,14 +114,12 @@ public void method() { sootClassTypeB, "method2", "void", Collections.emptyList()); MethodSignature candidate1 = - AbstractCallGraphAlgorithm.resolveConcreteDispatch(view, sootMethod1) - .orElse(null); + AbstractCallGraphAlgorithm.resolveConcreteDispatch(view, sootMethod1).orElse(null); assertNotNull(candidate1); assertEquals(candidate1, sootMethod1); MethodSignature candidate2 = - AbstractCallGraphAlgorithm.resolveConcreteDispatch(view, sootMethod2) - .orElse(null); + AbstractCallGraphAlgorithm.resolveConcreteDispatch(view, sootMethod2).orElse(null); assertNotNull(candidate2); assertEquals(candidate2, sootMethod3); @@ -134,9 +134,7 @@ public void method() { sootClassTypeA, "interfaceMethod", "void", Collections.emptyList()); MethodSignature candidateInterface = - AbstractCallGraphAlgorithm.resolveConcreteDispatch( - view, sootInterfaceMethodA) - .orElse(null); + AbstractCallGraphAlgorithm.resolveConcreteDispatch(view, sootInterfaceMethodA).orElse(null); assertNotNull(candidateInterface); assertEquals(candidateInterface, sootInterfaceMethod); @@ -151,8 +149,7 @@ public void method() { sootClassTypeA, "superInterfaceMethod", "void", Collections.emptyList()); MethodSignature candidateSuperInterface = - AbstractCallGraphAlgorithm.resolveConcreteDispatch( - view, sootSuperInterfaceMethodA) + AbstractCallGraphAlgorithm.resolveConcreteDispatch(view, sootSuperInterfaceMethodA) .orElse(null); assertNotNull(candidateSuperInterface); assertEquals(candidateSuperInterface, sootSuperInterfaceMethod); @@ -173,14 +170,12 @@ public void method() { sootClassTypeC, "interfaceMethod", "void", Collections.emptyList()); MethodSignature candidateSubInterface = - AbstractCallGraphAlgorithm.resolveConcreteDispatch( - view, sootSuperInterfaceMethodD) + AbstractCallGraphAlgorithm.resolveConcreteDispatch(view, sootSuperInterfaceMethodD) .orElse(null); assertNotNull(candidateSubInterface); assertEquals(candidateSubInterface, subInterfaceMethod); MethodSignature candidateSubInterface2 = - AbstractCallGraphAlgorithm.resolveConcreteDispatch( - view, sootSuperInterfaceMethodC) + AbstractCallGraphAlgorithm.resolveConcreteDispatch(view, sootSuperInterfaceMethodC) .orElse(null); assertNotNull(candidateSubInterface2); assertEquals(candidateSubInterface, candidateSubInterface2); diff --git a/sootup.core/src/main/java/sootup/core/typehierarchy/MethodDispatchResolver.java b/sootup.core/src/main/java/sootup/core/typehierarchy/MethodDispatchResolver.java index 5c638a5f8bb..dae74046108 100644 --- a/sootup.core/src/main/java/sootup/core/typehierarchy/MethodDispatchResolver.java +++ b/sootup.core/src/main/java/sootup/core/typehierarchy/MethodDispatchResolver.java @@ -22,18 +22,9 @@ */ import com.google.common.collect.Sets; -import java.util.List; -import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; -import java.util.stream.Stream; import javax.annotation.Nonnull; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import sootup.core.IdentifierFactory; -import sootup.core.frontend.ResolveException; -import sootup.core.jimple.common.expr.JSpecialInvokeExpr; -import sootup.core.model.Method; import sootup.core.model.SootClass; import sootup.core.model.SootMethod; import sootup.core.signatures.MethodSignature; @@ -71,7 +62,6 @@ public static Set resolveAllDispatches( .collect(Collectors.toSet()); } - /** * Resolves all dispatches of a given call filtered by a set of given classes * @@ -106,5 +96,4 @@ public static Set resolveAllDispatchesInClasses( return signatureInClasses; } - } diff --git a/sootup.tests/src/test/java/sootup/tests/typehierarchy/ViewTypeHierarchyTest.java b/sootup.tests/src/test/java/sootup/tests/typehierarchy/ViewTypeHierarchyTest.java index d830eed46d5..df6bc16e8f7 100644 --- a/sootup.tests/src/test/java/sootup/tests/typehierarchy/ViewTypeHierarchyTest.java +++ b/sootup.tests/src/test/java/sootup/tests/typehierarchy/ViewTypeHierarchyTest.java @@ -51,7 +51,7 @@ public class ViewTypeHierarchyTest { @Before public void setup() { - String jarFile = MethodDispatchResolverTest.jarFile; + String jarFile = "../shared-test-resources/java-miniapps/MiniApp.jar"; assertTrue("File " + jarFile + " not found.", new File(jarFile).exists()); String currentClassPath = System.getProperty("java.class.path") From 231c8a010347c0e36491cf21be80e44ac83a60d5 Mon Sep 17 00:00:00 2001 From: Jonas Klauke Date: Thu, 16 Nov 2023 14:26:55 +0100 Subject: [PATCH 14/23] reworked the RTA algorithm --- .../callgraph/RapidTypeAnalysisAlgorithm.java | 157 ++++++++++-------- 1 file changed, 85 insertions(+), 72 deletions(-) diff --git a/sootup.callgraph/src/main/java/sootup/callgraph/RapidTypeAnalysisAlgorithm.java b/sootup.callgraph/src/main/java/sootup/callgraph/RapidTypeAnalysisAlgorithm.java index 95e858ec02f..ee5ca14c8a2 100644 --- a/sootup.callgraph/src/main/java/sootup/callgraph/RapidTypeAnalysisAlgorithm.java +++ b/sootup.callgraph/src/main/java/sootup/callgraph/RapidTypeAnalysisAlgorithm.java @@ -35,6 +35,7 @@ import sootup.core.model.SootClass; import sootup.core.model.SootMethod; import sootup.core.signatures.MethodSignature; +import sootup.core.signatures.MethodSubSignature; import sootup.core.typehierarchy.MethodDispatchResolver; import sootup.core.types.ClassType; import sootup.core.views.View; @@ -100,9 +101,9 @@ public CallGraph initialize(@Nonnull List entryPoints) { * * @param method this object contains the method body which is inspected. */ - protected void collectInstantiatedClassesInMethod(SootMethod method) { + protected List collectInstantiatedClassesInMethod(SootMethod method) { if (method == null || method.isAbstract() || method.isNative()) { - return; + return Collections.emptyList(); } Set instantiated = @@ -112,7 +113,10 @@ protected void collectInstantiatedClassesInMethod(SootMethod method) { .filter(value -> value instanceof JNewExpr) .map(value -> ((JNewExpr) value).getType()) .collect(Collectors.toSet()); + List newInstantiatedClassTypes = instantiated.stream().filter(classType -> !instantiatedClasses.contains(classType)).collect( + Collectors.toList()); instantiatedClasses.addAll(instantiated); + return newInstantiatedClassTypes; } /** @@ -121,77 +125,88 @@ protected void collectInstantiatedClassesInMethod(SootMethod method) { * is instantiated and if it contains an implementation of the methods called in the invoke * expression. * - * @param method the method object that contains the given invoke expression in the body. + * @param sourceMethod the method object that contains the given invoke expression in the body. * @param invokeExpr it contains 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 method, AbstractInvokeExpr invokeExpr) { - MethodSignature targetMethodSignature = invokeExpr.getMethodSignature(); - Stream result = Stream.of(targetMethodSignature); + protected Stream resolveCall(SootMethod sourceMethod, AbstractInvokeExpr invokeExpr) { + MethodSignature resolveBaseMethodSignature = invokeExpr.getMethodSignature(); + Stream result = Stream.of(resolveBaseMethodSignature); - SootMethod targetMethod = - view.getClass(targetMethodSignature.getDeclClassType()) - .flatMap(clazz -> clazz.getMethod(targetMethodSignature.getSubSignature())) - .orElseGet(() -> findMethodInHierarchy(view, targetMethodSignature)); + SootMethod concreteBaseMethod = findConcreteMethod(view, resolveBaseMethodSignature).orElse(null); - if (targetMethod == null - || MethodModifier.isStatic(targetMethod.getModifiers()) + if (concreteBaseMethod == null + || MethodModifier.isStatic(concreteBaseMethod.getModifiers()) || (invokeExpr instanceof JSpecialInvokeExpr)) { return result; } else { - Set notInstantiatedCallTargets = Sets.newHashSet(); - Set implAndOverrides = - MethodDispatchResolver.resolveAllDispatchesInClasses( - view, targetMethodSignature, instantiatedClasses, notInstantiatedCallTargets); - // the class of the actual method call is instantiated - boolean targetMethodClassIsInstantiated = - instantiatedClasses.contains(targetMethodSignature.getDeclClassType()); - - // add the targetMethod to the ignoredCalls - if (!targetMethodClassIsInstantiated) { - notInstantiatedCallTargets.add(targetMethodSignature); + if (instantiatedClasses.contains(resolveBaseMethodSignature.getDeclClassType())) { + return Stream.concat(Stream.of(concreteBaseMethod.getSignature()),resolveAllCallTargets(sourceMethod.getSignature(),resolveBaseMethodSignature)); } - - // save filtered calls to include them later when their class is instantiated - notInstantiatedCallTargets.forEach( - ignoredMethodSignature -> { - ClassType notInstantiatedClass = ignoredMethodSignature.getDeclClassType(); - List calls = ignoredCalls.get(notInstantiatedClass); - if (calls == null) { - calls = new ArrayList<>(); - calls.add(new Call(method.getSignature(), ignoredMethodSignature)); - ignoredCalls.put(notInstantiatedClass, calls); - } else { - calls.add(new Call(method.getSignature(), ignoredMethodSignature)); - } - }); - - // find the concrete dispatch of all possible dispatches - Set concreteCallTargets = - implAndOverrides.stream() - .map(methodSignature -> resolveConcreteDispatch(view, methodSignature)) - .filter(Optional::isPresent) - .map(Optional::get) - .collect(Collectors.toSet()); - - // add the concrete of the targetMethod if the class is instantiated - if (targetMethodClassIsInstantiated) { - resolveConcreteDispatch(view, targetMethodSignature).ifPresent(concreteCallTargets::add); + else { + saveIgnoredCall(sourceMethod.getSignature(),resolveBaseMethodSignature); + return resolveAllCallTargets(sourceMethod.getSignature(),resolveBaseMethodSignature); } + } + } + + /** Resolves all targets of the given signature of the call. + * Only instantiated classes are considered as target. + * All possible class of non instantiated classes are saved to the ignoredCall Hashmap, + * because the classes can be instantiated at a later time + * + * + * @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 + * @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) { + return view.getTypeHierarchy().subtypesOf(resolveBaseMethodSignature.getDeclClassType()).stream() + .map( + classType -> { + MethodSignature method = view.getIdentifierFactory().getMethodSignature(classType,resolveBaseMethodSignature.getSubSignature()); + if(instantiatedClasses.contains(classType)){ + return resolveConcreteDispatch(view,method); + } + else { + saveIgnoredCall(source,method); + return Optional.empty(); + } + }) + .filter(Optional::isPresent) + .map(Optional::get); + } - return concreteCallTargets.stream(); + /** This method saves an ignored call + * If this is the first ignored call of the class type in the target method, + * an entry for the class type is created in the ignoredCalls Hashmap + * + * @param source the source method of the call + * @param target the target method of the call + */ + private void saveIgnoredCall(MethodSignature source, MethodSignature target){ + ClassType notInstantiatedClass = target.getDeclClassType(); + List calls = ignoredCalls.get(notInstantiatedClass); + Call ignoredCall= new Call(source,target); + if (calls == null) { + calls = new ArrayList<>(); + ignoredCalls.put(notInstantiatedClass, calls); } + calls.add(ignoredCall); } /** * Preprocessing of a method in the RTA call graph algorithm * *

Before processing the method, all instantiated types are collected inside the body of the - * sourceMethod. + * sourceMethod. If a new instantiated class has previously ignored calls to this class, + * they are added to call graph * * @param view view * @param sourceMethod the processed method @@ -210,27 +225,8 @@ protected void preProcessingMethod( .orElse(null); if (method == null) return; - collectInstantiatedClassesInMethod(method); - } - - /** - * Postprocessing of a method in the RTA call graph algorithm - * - *

RTA has to add previously ignored calls because a found instantiation of a class could - * enable a call to a ignored method at a later time. - * - * @param view view - * @param sourceMethod the processed method - * @param workList the current worklist that is extended by methods that have to be analyzed. - * @param cg the current cg is extended by new call targets and calls - */ - @Override - protected void postProcessingMethod( - View> view, - MethodSignature sourceMethod, - @Nonnull Deque workList, - @Nonnull MutableCallGraph cg) { - instantiatedClasses.forEach( + List newInstantiatedClasses = collectInstantiatedClassesInMethod(method); + newInstantiatedClasses.forEach( instantiatedClassType -> { List newEdges = ignoredCalls.get(instantiatedClassType); if (newEdges != null) { @@ -256,4 +252,21 @@ protected void postProcessingMethod( } }); } + + /** + * Postprocessing is not needed in RTA + * + * @param view view + * @param sourceMethod the processed method + * @param workList the current worklist that is extended by methods that have to be analyzed. + * @param cg the current cg is extended by new call targets and calls + */ + @Override + protected void postProcessingMethod( + View> view, + MethodSignature sourceMethod, + @Nonnull Deque workList, + @Nonnull MutableCallGraph cg) { +// not needed + } } From 36a0da6ae9396de269c7b33d36c9ebd7e6ef737f Mon Sep 17 00:00:00 2001 From: Jonas Klauke Date: Thu, 16 Nov 2023 14:28:35 +0100 Subject: [PATCH 15/23] removed unnecessary utility method that is moved to the RTA call graph algorithm --- .../callgraph/RapidTypeAnalysisAlgorithm.java | 78 ++++++++++--------- .../typehierarchy/MethodDispatchResolver.java | 35 --------- 2 files changed, 41 insertions(+), 72 deletions(-) diff --git a/sootup.callgraph/src/main/java/sootup/callgraph/RapidTypeAnalysisAlgorithm.java b/sootup.callgraph/src/main/java/sootup/callgraph/RapidTypeAnalysisAlgorithm.java index ee5ca14c8a2..5bc3b472f2a 100644 --- a/sootup.callgraph/src/main/java/sootup/callgraph/RapidTypeAnalysisAlgorithm.java +++ b/sootup.callgraph/src/main/java/sootup/callgraph/RapidTypeAnalysisAlgorithm.java @@ -22,7 +22,6 @@ * #L% */ -import com.google.common.collect.Sets; import java.util.*; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -35,8 +34,6 @@ import sootup.core.model.SootClass; import sootup.core.model.SootMethod; import sootup.core.signatures.MethodSignature; -import sootup.core.signatures.MethodSubSignature; -import sootup.core.typehierarchy.MethodDispatchResolver; import sootup.core.types.ClassType; import sootup.core.views.View; @@ -113,8 +110,10 @@ protected List collectInstantiatedClassesInMethod(SootMethod method) .filter(value -> value instanceof JNewExpr) .map(value -> ((JNewExpr) value).getType()) .collect(Collectors.toSet()); - List newInstantiatedClassTypes = instantiated.stream().filter(classType -> !instantiatedClasses.contains(classType)).collect( - Collectors.toList()); + List newInstantiatedClassTypes = + instantiated.stream() + .filter(classType -> !instantiatedClasses.contains(classType)) + .collect(Collectors.toList()); instantiatedClasses.addAll(instantiated); return newInstantiatedClassTypes; } @@ -132,11 +131,13 @@ protected List collectInstantiatedClassesInMethod(SootMethod method) */ @Override @Nonnull - protected Stream resolveCall(SootMethod sourceMethod, AbstractInvokeExpr invokeExpr) { + protected Stream resolveCall( + SootMethod sourceMethod, AbstractInvokeExpr invokeExpr) { MethodSignature resolveBaseMethodSignature = invokeExpr.getMethodSignature(); Stream result = Stream.of(resolveBaseMethodSignature); - SootMethod concreteBaseMethod = findConcreteMethod(view, resolveBaseMethodSignature).orElse(null); + SootMethod concreteBaseMethod = + findConcreteMethod(view, resolveBaseMethodSignature).orElse(null); if (concreteBaseMethod == null || MethodModifier.isStatic(concreteBaseMethod.getModifiers()) @@ -145,37 +146,40 @@ protected Stream resolveCall(SootMethod sourceMethod, AbstractI } else { // the class of the actual method call is instantiated if (instantiatedClasses.contains(resolveBaseMethodSignature.getDeclClassType())) { - return Stream.concat(Stream.of(concreteBaseMethod.getSignature()),resolveAllCallTargets(sourceMethod.getSignature(),resolveBaseMethodSignature)); - } - else { - saveIgnoredCall(sourceMethod.getSignature(),resolveBaseMethodSignature); - return resolveAllCallTargets(sourceMethod.getSignature(),resolveBaseMethodSignature); + return Stream.concat( + Stream.of(concreteBaseMethod.getSignature()), + resolveAllCallTargets(sourceMethod.getSignature(), resolveBaseMethodSignature)); + } else { + saveIgnoredCall(sourceMethod.getSignature(), resolveBaseMethodSignature); + return resolveAllCallTargets(sourceMethod.getSignature(), resolveBaseMethodSignature); } } } - /** Resolves all targets of the given signature of the call. - * Only instantiated classes are considered as target. - * All possible class of non instantiated classes are saved to the ignoredCall Hashmap, - * because the classes can be instantiated at a later time - * + /** + * Resolves all targets of the given signature of the call. Only instantiated classes are + * considered as target. All possible class of non instantiated classes are saved to the + * ignoredCall Hashmap, because the classes can be instantiated at a later time * * @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 - * @return a stream of all method signatures of instantiated classes - * that can be resolved as target from the given base method signature. + * @param resolveBaseMethodSignature the base of the resolving. All subtypes of the declaring + * class are analyzed as potential targets + * @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) { - return view.getTypeHierarchy().subtypesOf(resolveBaseMethodSignature.getDeclClassType()).stream() + private Stream resolveAllCallTargets( + MethodSignature source, MethodSignature resolveBaseMethodSignature) { + return view.getTypeHierarchy().subtypesOf(resolveBaseMethodSignature.getDeclClassType()) + .stream() .map( classType -> { - MethodSignature method = view.getIdentifierFactory().getMethodSignature(classType,resolveBaseMethodSignature.getSubSignature()); - if(instantiatedClasses.contains(classType)){ - return resolveConcreteDispatch(view,method); - } - else { - saveIgnoredCall(source,method); + MethodSignature method = + view.getIdentifierFactory() + .getMethodSignature(classType, resolveBaseMethodSignature.getSubSignature()); + if (instantiatedClasses.contains(classType)) { + return resolveConcreteDispatch(view, method); + } else { + saveIgnoredCall(source, method); return Optional.empty(); } }) @@ -183,17 +187,17 @@ private Stream resolveAllCallTargets( MethodSignature source, .map(Optional::get); } - /** This method saves an ignored call - * If this is the first ignored call of the class type in the target method, - * an entry for the class type is created in the ignoredCalls Hashmap + /** + * This method saves an ignored call If this is the first ignored call of the class type in the + * target method, an entry for the class type is created in the ignoredCalls Hashmap * * @param source the source method of the call * @param target the target method of the call */ - private void saveIgnoredCall(MethodSignature source, MethodSignature target){ + private void saveIgnoredCall(MethodSignature source, MethodSignature target) { ClassType notInstantiatedClass = target.getDeclClassType(); List calls = ignoredCalls.get(notInstantiatedClass); - Call ignoredCall= new Call(source,target); + Call ignoredCall = new Call(source, target); if (calls == null) { calls = new ArrayList<>(); ignoredCalls.put(notInstantiatedClass, calls); @@ -205,8 +209,8 @@ private void saveIgnoredCall(MethodSignature source, MethodSignature target){ * Preprocessing of a method in the RTA call graph algorithm * *

Before processing the method, all instantiated types are collected inside the body of the - * sourceMethod. If a new instantiated class has previously ignored calls to this class, - * they are added to call graph + * sourceMethod. If a new instantiated class has previously ignored calls to this class, they are + * added to call graph * * @param view view * @param sourceMethod the processed method @@ -267,6 +271,6 @@ protected void postProcessingMethod( MethodSignature sourceMethod, @Nonnull Deque workList, @Nonnull MutableCallGraph cg) { -// not needed + // not needed } } diff --git a/sootup.core/src/main/java/sootup/core/typehierarchy/MethodDispatchResolver.java b/sootup.core/src/main/java/sootup/core/typehierarchy/MethodDispatchResolver.java index dae74046108..de8989b9954 100644 --- a/sootup.core/src/main/java/sootup/core/typehierarchy/MethodDispatchResolver.java +++ b/sootup.core/src/main/java/sootup/core/typehierarchy/MethodDispatchResolver.java @@ -61,39 +61,4 @@ public static Set resolveAllDispatches( view.getIdentifierFactory().getMethodSignature(classType, m.getSubSignature())) .collect(Collectors.toSet()); } - - /** - * Resolves all dispatches of a given call filtered by a set of given classes - * - *

searches the view for classes that can override the method m and returns the - * set of method signatures that a method call could resolve to within the given classes. All - * filtered signatures are added to the given set filteredSignatures. - * - * @param view it contains all classes and their connections. - * @param m it defines the actual invoked method signature. - * @param classes the set of classes that define possible dispatch targets of method signatures - * @param filteredSignatures the set of method signatures which is filled with filtered method - * signatures in the execution of this method. - * @return a set of method signatures that a method call could resolve to within the given classes - */ - @Nonnull - public static Set resolveAllDispatchesInClasses( - View> view, - MethodSignature m, - Set classes, - Set filteredSignatures) { - - Set allSignatures = resolveAllDispatches(view, m); - Set signatureInClasses = Sets.newHashSet(); - allSignatures.forEach( - methodSignature -> { - if (classes.contains(methodSignature.getDeclClassType())) { - signatureInClasses.add(methodSignature); - } else { - filteredSignatures.add(methodSignature); - } - }); - - return signatureInClasses; - } } From d64a7be786484ef6cd6ad19d0c28957a45115e45 Mon Sep 17 00:00:00 2001 From: Jonas Klauke Date: Thu, 16 Nov 2023 16:40:39 +0100 Subject: [PATCH 16/23] ICFGDotExporter uses call graph for dispatches. removed unnecessary utility function --- .../interprocedural/icfg/ICFGDotExporter.java | 105 +++++++++++------- .../icfg/JimpleBasedInterproceduralCFG.java | 19 +++- .../icfg/ICFGDotExporterTest.java | 2 +- .../typehierarchy/MethodDispatchResolver.java | 64 ----------- 4 files changed, 80 insertions(+), 110 deletions(-) delete mode 100644 sootup.core/src/main/java/sootup/core/typehierarchy/MethodDispatchResolver.java 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 a7678c05681..1c0a4c563a8 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 @@ -1,8 +1,30 @@ package sootup.analysis.interprocedural.icfg; +/*- + * #%L + * Soot - a J*va Optimization Framework + * %% + * Copyright (C) 2022-2023 Palaniappan Muthuraman, Jonas Klauke + * %% + * 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.*; import java.util.stream.Collectors; -import sootup.callgraph.AbstractCallGraphAlgorithm; +import sootup.callgraph.CallGraph; import sootup.core.graph.BasicBlock; import sootup.core.graph.StmtGraph; import sootup.core.jimple.common.expr.JNewExpr; @@ -12,7 +34,6 @@ import sootup.core.model.SootMethod; import sootup.core.signatures.MethodSignature; import sootup.core.signatures.MethodSubSignature; -import sootup.core.typehierarchy.MethodDispatchResolver; import sootup.core.types.VoidType; import sootup.core.util.DotExporter; import sootup.core.views.View; @@ -20,11 +41,13 @@ public class ICFGDotExporter { public static String buildICFGGraph( - Map signatureToStmtGraph, View> view) { + Map signatureToStmtGraph, + View> view, + CallGraph callGraph) { final StringBuilder sb = new StringBuilder(); DotExporter.buildDiGraphObject(sb); Map calls; - calls = computeCalls(signatureToStmtGraph, view); + calls = computeCalls(signatureToStmtGraph, view, callGraph); for (Map.Entry entry : signatureToStmtGraph.entrySet()) { String graph = DotExporter.buildGraph(entry.getValue(), true, calls, entry.getKey()); sb.append(graph + "\n"); @@ -38,9 +61,13 @@ public static String buildICFGGraph( * methods. */ public static Map computeCalls( - Map stmtGraphSet, View> view) { + Map stmtGraphSet, + View> view, + CallGraph callgraph) { Map calls = new HashMap<>(); - for (StmtGraph stmtGraph : stmtGraphSet.values()) { + for (Map.Entry entry : stmtGraphSet.entrySet()) { + StmtGraph stmtGraph = entry.getValue(); + MethodSignature source = entry.getKey(); Collection> blocks; try { blocks = stmtGraph.getBlocksSorted(); @@ -51,11 +78,11 @@ public static Map computeCalls( List stmts = block.getStmts(); for (Stmt stmt : stmts) { if (stmt.containsInvokeExpr()) { - MethodSignature methodSignature = stmt.getInvokeExpr().getMethodSignature(); + MethodSignature target = stmt.getInvokeExpr().getMethodSignature(); int hashCode = stmt.hashCode(); - calls.put(hashCode, methodSignature); + calls.put(hashCode, target); // compute all the classes that are made to the subclasses as well - connectEdgesToSubClasses(methodSignature, view, calls); + connectEdgesToSubClasses(source, target, view, calls, callgraph); } else if (stmt instanceof JAssignStmt) { JAssignStmt jAssignStmt = (JAssignStmt) stmt; Integer currentHashCode = stmt.hashCode(); @@ -86,44 +113,40 @@ public static Map computeCalls( } public static Set getMethodSignatureInSubClass( - MethodSignature targetMethodSignature, View> view) { - try { - return MethodDispatchResolver.resolveAllDispatches(view, targetMethodSignature).stream() - .map( - methodSignature -> - AbstractCallGraphAlgorithm.resolveConcreteDispatch(view, methodSignature)) - .filter(Optional::isPresent) - .map(Optional::get) - .collect(Collectors.toSet()); - } catch (Exception e) { - return null; + MethodSignature source, MethodSignature target, CallGraph callGraph) { + if (!callGraph.containsMethod(source) || !callGraph.containsMethod(target)) { + return Collections.emptySet(); } + return callGraph.callsFrom(source).stream() + .filter( + methodSignature -> methodSignature.getSubSignature().equals(target.getSubSignature())) + .collect(Collectors.toSet()); } public static void connectEdgesToSubClasses( - MethodSignature methodSignature, + MethodSignature source, + MethodSignature target, View> view, - Map calls) { + Map calls, + CallGraph callgraph) { Set methodSignatureInSubClass = - getMethodSignatureInSubClass(methodSignature, view); - if (methodSignatureInSubClass != null) { - methodSignatureInSubClass.forEach( - subclassmethodSignature -> { - Optional method = view.getMethod(methodSignature); - MethodSignature initMethod = - new MethodSignature( - subclassmethodSignature.getDeclClassType(), - new MethodSubSignature( - "", Collections.emptyList(), VoidType.getInstance())); - if (method.isPresent() - && !subclassmethodSignature.toString().equals(initMethod.toString())) { - if (method.get().hasBody()) { - calls.put( - method.get().getBody().getStmtGraph().getStartingStmt().hashCode(), - subclassmethodSignature); - } + getMethodSignatureInSubClass(source, target, callgraph); + methodSignatureInSubClass.forEach( + subclassmethodSignature -> { + Optional method = view.getMethod(target); + MethodSignature initMethod = + new MethodSignature( + subclassmethodSignature.getDeclClassType(), + new MethodSubSignature( + "", Collections.emptyList(), VoidType.getInstance())); + if (method.isPresent() + && !subclassmethodSignature.toString().equals(initMethod.toString())) { + if (method.get().hasBody()) { + calls.put( + method.get().getBody().getStmtGraph().getStartingStmt().hashCode(), + subclassmethodSignature); } - }); - } + } + }); } } 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 1e9faa7cc42..fc675e9f5bb 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 @@ -152,13 +152,23 @@ public JimpleBasedInterproceduralCFG( public String buildICFGGraph(CallGraph callGraph) { Map signatureToStmtGraph = new LinkedHashMap<>(); computeAllCalls(mainMethodSignature, signatureToStmtGraph, callGraph); - return ICFGDotExporter.buildICFGGraph(signatureToStmtGraph, view); + return ICFGDotExporter.buildICFGGraph(signatureToStmtGraph, view, callGraph); } public void computeAllCalls( MethodSignature methodSignature, Map signatureToStmtGraph, CallGraph callGraph) { + ArrayList visitedMethods = new ArrayList<>(); + computeAllCalls(methodSignature, signatureToStmtGraph, callGraph, visitedMethods); + } + + private void computeAllCalls( + MethodSignature methodSignature, + Map signatureToStmtGraph, + CallGraph callGraph, + List visitedMethods) { + visitedMethods.add(methodSignature); final Optional methodOpt = view.getMethod(methodSignature); // return if the methodSignature is already added to the hashMap to avoid stackoverflow error. if (signatureToStmtGraph.containsKey(methodSignature)) return; @@ -169,11 +179,12 @@ public void computeAllCalls( signatureToStmtGraph.put(methodSignature, stmtGraph); } } - callGraph - .callsFrom(methodSignature) + callGraph.callsFrom(methodSignature).stream() + .filter(methodSignature1 -> !visitedMethods.contains(methodSignature1)) .forEach( nextMethodSignature -> - computeAllCalls(nextMethodSignature, signatureToStmtGraph, callGraph)); + computeAllCalls( + nextMethodSignature, signatureToStmtGraph, callGraph, visitedMethods)); } private CallGraph initCallGraph() { diff --git a/sootup.analysis/src/test/java/sootup/analysis/interprocedural/icfg/ICFGDotExporterTest.java b/sootup.analysis/src/test/java/sootup/analysis/interprocedural/icfg/ICFGDotExporterTest.java index 2a009388e75..25bddb3617e 100644 --- a/sootup.analysis/src/test/java/sootup/analysis/interprocedural/icfg/ICFGDotExporterTest.java +++ b/sootup.analysis/src/test/java/sootup/analysis/interprocedural/icfg/ICFGDotExporterTest.java @@ -187,7 +187,7 @@ public String edgesFromCallGraph( Map signatureToStmtGraph = new LinkedHashMap<>(); icfg.computeAllCalls(methodSignature, signatureToStmtGraph, callGraph); Map calls; - calls = ICFGDotExporter.computeCalls(signatureToStmtGraph, view); + calls = ICFGDotExporter.computeCalls(signatureToStmtGraph, view, callGraph); final Optional methodOpt = view.getMethod(methodSignature); if (methodOpt.isPresent()) { SootMethod sootMethod = methodOpt.get(); diff --git a/sootup.core/src/main/java/sootup/core/typehierarchy/MethodDispatchResolver.java b/sootup.core/src/main/java/sootup/core/typehierarchy/MethodDispatchResolver.java deleted file mode 100644 index de8989b9954..00000000000 --- a/sootup.core/src/main/java/sootup/core/typehierarchy/MethodDispatchResolver.java +++ /dev/null @@ -1,64 +0,0 @@ -package sootup.core.typehierarchy; -/*- - * #%L - * Soot - a J*va Optimization Framework - * %% - * Copyright (C) 2019-2022 Christian Brüggemann, Jonas Klauke - * %% - * 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 com.google.common.collect.Sets; -import java.util.Set; -import java.util.stream.Collectors; -import javax.annotation.Nonnull; -import sootup.core.model.SootClass; -import sootup.core.model.SootMethod; -import sootup.core.signatures.MethodSignature; -import sootup.core.types.ClassType; -import sootup.core.views.View; - -public final class MethodDispatchResolver { - - private MethodDispatchResolver() {} - - /** - * Searches the view for classes that are subtypes of the class contained in the signature. - * returns method signatures to all subtypes. Abstract methods are filtered the returned set can - * contain signatures of not implemented methods. - */ - @Nonnull - public static Set resolveAllDispatches( - View> view, MethodSignature m) { - TypeHierarchy hierarchy = view.getTypeHierarchy(); - - return hierarchy.subtypesOf(m.getDeclClassType()).stream() - .filter( - classType -> { - SootMethod sootMethod = - view.getMethod( - view.getIdentifierFactory() - .getMethodSignature(classType, m.getSubSignature())) - .orElse(null); - // methods are kept that are not implemented or not abstract - return sootMethod == null || !sootMethod.isAbstract(); - }) - .map( - classType -> - view.getIdentifierFactory().getMethodSignature(classType, m.getSubSignature())) - .collect(Collectors.toSet()); - } -} From bbee224f92023a9b43c6ba5cc3b299a93975ee53 Mon Sep 17 00:00:00 2001 From: Jonas Klauke Date: Thu, 16 Nov 2023 17:55:44 +0100 Subject: [PATCH 17/23] Callgraph Algorithm stops at methods marked as Library. Added Test for it --- .../callgraph/AbstractCallGraphAlgorithm.java | 9 ++-- .../sootup/callgraph/CallGraphTestBase.java | 39 ++++++++++++++++++ .../callgraph/Library/binary/Application.java | 14 +++++++ .../callgraph/Library/binary/Library.java | 18 ++++++++ .../binary/application/app/Application.class | Bin 0 -> 392 bytes .../Library/binary/library/lib/Library.class | Bin 0 -> 424 bytes 6 files changed, 77 insertions(+), 3 deletions(-) create mode 100644 sootup.callgraph/src/test/resources/callgraph/Library/binary/Application.java create mode 100644 sootup.callgraph/src/test/resources/callgraph/Library/binary/Library.java create mode 100644 sootup.callgraph/src/test/resources/callgraph/Library/binary/application/app/Application.class create mode 100644 sootup.callgraph/src/test/resources/callgraph/Library/binary/library/lib/Library.class diff --git a/sootup.callgraph/src/main/java/sootup/callgraph/AbstractCallGraphAlgorithm.java b/sootup.callgraph/src/main/java/sootup/callgraph/AbstractCallGraphAlgorithm.java index f883f283441..c2d796bce61 100644 --- a/sootup.callgraph/src/main/java/sootup/callgraph/AbstractCallGraphAlgorithm.java +++ b/sootup.callgraph/src/main/java/sootup/callgraph/AbstractCallGraphAlgorithm.java @@ -146,6 +146,11 @@ final void processWorkList( // skip if already processed if (processed.contains(currentMethodSignature)) continue; + // skip if library class + SootClass currentClass = + view.getClass(currentMethodSignature.getDeclClassType()).orElse(null); + if (currentClass == null || currentClass.isLibraryClass()) continue; + // perform pre-processing if needed preProcessingMethod(view, currentMethodSignature, workList, cg); @@ -154,9 +159,7 @@ final void processWorkList( // transform the method signature to the actual SootMethod SootMethod currentMethod = - view.getClass(currentMethodSignature.getDeclClassType()) - .flatMap(c -> c.getMethod(currentMethodSignature.getSubSignature())) - .orElse(null); + currentClass.getMethod(currentMethodSignature.getSubSignature()).orElse(null); // get all call targets of invocations in the method body Stream invocationTargets = resolveAllCallsFromSourceMethod(currentMethod); diff --git a/sootup.callgraph/src/test/java/sootup/callgraph/CallGraphTestBase.java b/sootup.callgraph/src/test/java/sootup/callgraph/CallGraphTestBase.java index cf0421898e7..69a69770623 100644 --- a/sootup.callgraph/src/test/java/sootup/callgraph/CallGraphTestBase.java +++ b/sootup.callgraph/src/test/java/sootup/callgraph/CallGraphTestBase.java @@ -714,4 +714,43 @@ public void testNoMainMethod() { "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."); } } + + /** + * Test uses initialize() method to create call graph, but no main method is present in input java + * source files. Expected result is RuntimeException. + */ + @Test + public void testStopAtLibraryClass() { + + String classPath = "src/test/resources/callgraph/Library/binary/"; + JavaProject.JavaProjectBuilder javaProjectBuilder = + JavaProject.builder(new JavaLanguage(8)) + .addInputLocation( + new JavaClassPathAnalysisInputLocation( + System.getProperty("java.home") + "/lib/rt.jar", SourceType.Library)) + .addInputLocation( + new JavaClassPathAnalysisInputLocation( + classPath + "application/", SourceType.Application)) + .addInputLocation( + new JavaClassPathAnalysisInputLocation(classPath + "library/", SourceType.Library)); + JavaView view = javaProjectBuilder.build().createView(); + + MethodSignature mainMethodSignature = + identifierFactory.getMethodSignature( + "app.Application", "main", "void", Collections.singletonList("java.lang.String[]")); + CallGraphAlgorithm algorithm = createAlgorithm(view); + CallGraph cg = algorithm.initialize(Collections.singletonList(mainMethodSignature)); + + assertTrue(cg.callsFrom(mainMethodSignature).size() > 0); + + SootClass libraryClass = + view.getClass(view.getIdentifierFactory().getClassType("lib.Library")).orElse(null); + assertNotNull(libraryClass); + for (SootMethod method : libraryClass.getMethods()) { + MethodSignature ms = method.getSignature(); + if (cg.containsMethod(ms)) { + assertEquals(0, cg.callsFrom(method.getSignature()).size()); + } + } + } } diff --git a/sootup.callgraph/src/test/resources/callgraph/Library/binary/Application.java b/sootup.callgraph/src/test/resources/callgraph/Library/binary/Application.java new file mode 100644 index 00000000000..7fba38aef09 --- /dev/null +++ b/sootup.callgraph/src/test/resources/callgraph/Library/binary/Application.java @@ -0,0 +1,14 @@ +package app; + +import lib.Library; +public class Application { + + public static void main(String[] args) { + Library lib = new Library(); + lib.use(); + test(); + } + public static void test() { + + } +} diff --git a/sootup.callgraph/src/test/resources/callgraph/Library/binary/Library.java b/sootup.callgraph/src/test/resources/callgraph/Library/binary/Library.java new file mode 100644 index 00000000000..35e7b975a9b --- /dev/null +++ b/sootup.callgraph/src/test/resources/callgraph/Library/binary/Library.java @@ -0,0 +1,18 @@ +package lib; + +public class Library { + + public Library (){ + c(); + } + public void use() { + a(); + b(); + } + public void a() { + } + public void b() { + } + public void c() { + } +} \ No newline at end of file diff --git a/sootup.callgraph/src/test/resources/callgraph/Library/binary/application/app/Application.class b/sootup.callgraph/src/test/resources/callgraph/Library/binary/application/app/Application.class new file mode 100644 index 0000000000000000000000000000000000000000..8d99354bc4984fb43d5108864a67111d3584bcfc GIT binary patch literal 392 zcmZWl!AiqG5Ph3$vrUX9ty=Zq!CO@@m);aX1W!Q^6+t|0;!?Jxn~)}gpXJ4hf*;^V ziL*th;2vgX=goU>XTQGRKLL!e8$h5Z&=26CXTxRy4_gA;0s{u`OdH*tGq}UUI|hDP zJ}QPN)ka;nvrN^uGApRVXHpx6!SErS$|so=(oB+ZQ)@FhT@o8LZy17c+19zb(v}ln zR8^sK+33=oSXPKGe1^3`XGyBFTGr1YcHoEDu>D9@Rq}^J3M;RhY^ri{aND`U0j(ft zKfndEq7(zV?NeryJ4Xw!7b?h%qPJ{v8Vj*bwH+PNS2&d84>=A0uKO<`q5#S*H0UrxK!~g&Q literal 0 HcmV?d00001 diff --git a/sootup.callgraph/src/test/resources/callgraph/Library/binary/library/lib/Library.class b/sootup.callgraph/src/test/resources/callgraph/Library/binary/library/lib/Library.class new file mode 100644 index 0000000000000000000000000000000000000000..277179fbfde8a7d0bf10529e8b8e48c31d7031bb GIT binary patch literal 424 zcmaivJ4*vm5QWcVH~Y$(O?+Z$XHvLLO0f~Ff)(uOi*_JWRug7nPN%Ps=^DKk7939w5s-a^G$yog$Un9< u0@}jAwv@Jv@&Rq!lGJI3-_1GTS03>Tyg~GiO|pUl9==I7rl4Tg9!6g`4K5Y{ literal 0 HcmV?d00001 From c553668fb7e1d874341a0247d57c08a949454eca Mon Sep 17 00:00:00 2001 From: Jonas Klauke Date: Thu, 16 Nov 2023 17:56:55 +0100 Subject: [PATCH 18/23] improved library test --- .../src/test/java/sootup/callgraph/CallGraphTestBase.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sootup.callgraph/src/test/java/sootup/callgraph/CallGraphTestBase.java b/sootup.callgraph/src/test/java/sootup/callgraph/CallGraphTestBase.java index 69a69770623..259ea49daf1 100644 --- a/sootup.callgraph/src/test/java/sootup/callgraph/CallGraphTestBase.java +++ b/sootup.callgraph/src/test/java/sootup/callgraph/CallGraphTestBase.java @@ -741,7 +741,7 @@ public void testStopAtLibraryClass() { CallGraphAlgorithm algorithm = createAlgorithm(view); CallGraph cg = algorithm.initialize(Collections.singletonList(mainMethodSignature)); - assertTrue(cg.callsFrom(mainMethodSignature).size() > 0); + assertTrue(!cg.callsFrom(mainMethodSignature).isEmpty()); SootClass libraryClass = view.getClass(view.getIdentifierFactory().getClassType("lib.Library")).orElse(null); From 4e83872305e3509b2e323534cb786f15697ca5d4 Mon Sep 17 00:00:00 2001 From: Jonas Klauke Date: Fri, 17 Nov 2023 10:42:46 +0100 Subject: [PATCH 19/23] adapted tests to the ignored Library classes in the call graph algorithm --- .../test/java/sootup/callgraph/CallGraphTestBase.java | 4 ++-- .../callgraph/ClassHierarchyAnalysisAlgorithmTest.java | 10 ++-------- .../callgraph/RapidTypeAnalysisAlgorithmTest.java | 10 ++-------- 3 files changed, 6 insertions(+), 18 deletions(-) diff --git a/sootup.callgraph/src/test/java/sootup/callgraph/CallGraphTestBase.java b/sootup.callgraph/src/test/java/sootup/callgraph/CallGraphTestBase.java index 259ea49daf1..7e814509f86 100644 --- a/sootup.callgraph/src/test/java/sootup/callgraph/CallGraphTestBase.java +++ b/sootup.callgraph/src/test/java/sootup/callgraph/CallGraphTestBase.java @@ -130,8 +130,8 @@ public void testRecursiveCall() { assertTrue(cg.containsMethod(mainMethodSignature)); assertTrue(cg.containsMethod(method)); assertFalse(cg.containsMethod(uncalledMethod)); - // 2 methods + Object::clinit + Object::registerNatives - TestCase.assertEquals(4, cg.getMethodSignatures().size()); + // 2 methods + Object::clinit + TestCase.assertEquals(3, cg.getMethodSignatures().size()); assertTrue(cg.containsCall(mainMethodSignature, mainMethodSignature)); assertTrue(cg.containsCall(mainMethodSignature, method)); diff --git a/sootup.callgraph/src/test/java/sootup/callgraph/ClassHierarchyAnalysisAlgorithmTest.java b/sootup.callgraph/src/test/java/sootup/callgraph/ClassHierarchyAnalysisAlgorithmTest.java index 88d8b83677e..403ffae5674 100644 --- a/sootup.callgraph/src/test/java/sootup/callgraph/ClassHierarchyAnalysisAlgorithmTest.java +++ b/sootup.callgraph/src/test/java/sootup/callgraph/ClassHierarchyAnalysisAlgorithmTest.java @@ -188,7 +188,7 @@ public void testMiscExample1() { assertEquals( cg.toString().replace("\n", "").replace("\t", ""), - "GraphBasedCallGraph(16):" + "GraphBasedCallGraph(14):" + "()>:" + "to ()>" + "from ()>" @@ -238,15 +238,9 @@ public void testMiscExample1() { + "to ()>" + "" + "()>:" - + "to ()>" - + "to " + "from " - + "from ()>" + "" + "()>:" - + "from ()>" - + "" - + ":" - + "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 ef03efc3265..24876c7e1b3 100644 --- a/sootup.callgraph/src/test/java/sootup/callgraph/RapidTypeAnalysisAlgorithmTest.java +++ b/sootup.callgraph/src/test/java/sootup/callgraph/RapidTypeAnalysisAlgorithmTest.java @@ -180,7 +180,7 @@ public void testMiscExample1() { assertEquals( cg.toString().replace("\n", "").replace("\t", ""), - "GraphBasedCallGraph(15):" + "GraphBasedCallGraph(13):" + "()>:" + "to ()>" + "from ()>" @@ -226,16 +226,10 @@ public void testMiscExample1() { + "to ()>" + "" + "()>:" - + "to ()>" - + "to " + "from " - + "from ()>" + "" + "()>:" - + "from ()>" - + "" - + ":" - + "from ()>"); + + "from ()>"); } @Test From 3113dcf99fab5677de9c4a509168fe452a68ed72 Mon Sep 17 00:00:00 2001 From: Jonas Klauke Date: Fri, 17 Nov 2023 10:45:30 +0100 Subject: [PATCH 20/23] removed warnings in testcases --- .../java/sootup/callgraph/CallGraphTestBase.java | 2 +- .../ClassHierarchyAnalysisAlgorithmTest.java | 12 ------------ .../callgraph/RapidTypeAnalysisAlgorithmTest.java | 11 ----------- 3 files changed, 1 insertion(+), 24 deletions(-) diff --git a/sootup.callgraph/src/test/java/sootup/callgraph/CallGraphTestBase.java b/sootup.callgraph/src/test/java/sootup/callgraph/CallGraphTestBase.java index 7e814509f86..26c41ee37b8 100644 --- a/sootup.callgraph/src/test/java/sootup/callgraph/CallGraphTestBase.java +++ b/sootup.callgraph/src/test/java/sootup/callgraph/CallGraphTestBase.java @@ -741,7 +741,7 @@ public void testStopAtLibraryClass() { CallGraphAlgorithm algorithm = createAlgorithm(view); CallGraph cg = algorithm.initialize(Collections.singletonList(mainMethodSignature)); - assertTrue(!cg.callsFrom(mainMethodSignature).isEmpty()); + assertFalse(cg.callsFrom(mainMethodSignature).isEmpty()); SootClass libraryClass = view.getClass(view.getIdentifierFactory().getClassType("lib.Library")).orElse(null); diff --git a/sootup.callgraph/src/test/java/sootup/callgraph/ClassHierarchyAnalysisAlgorithmTest.java b/sootup.callgraph/src/test/java/sootup/callgraph/ClassHierarchyAnalysisAlgorithmTest.java index 403ffae5674..f9c6bdc1875 100644 --- a/sootup.callgraph/src/test/java/sootup/callgraph/ClassHierarchyAnalysisAlgorithmTest.java +++ b/sootup.callgraph/src/test/java/sootup/callgraph/ClassHierarchyAnalysisAlgorithmTest.java @@ -194,38 +194,28 @@ public void testMiscExample1() { + "from ()>" + "from ()>" + "from ()>" - + "" + ":" + "from " - + "" + "()>:" + "to ()>" + "from " - + "" + ":" + "from " - + "" + ":" + "from " - + "" + "()>:" + "to ()>" + "from " - + "" + "()>:" + "to ()>" + "from ()>" - + "" + ":" + "from " - + "" + "()>:" + "to ()>" + "from " - + "" + ":" + "from " - + "" + ":" + "to " + "to ()>" @@ -236,10 +226,8 @@ public void testMiscExample1() { + "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 24876c7e1b3..19781a95323 100644 --- a/sootup.callgraph/src/test/java/sootup/callgraph/RapidTypeAnalysisAlgorithmTest.java +++ b/sootup.callgraph/src/test/java/sootup/callgraph/RapidTypeAnalysisAlgorithmTest.java @@ -186,35 +186,26 @@ public void testMiscExample1() { + "from ()>" + "from ()>" + "from ()>" - + "" + "()>:" + "to ()>" + "from " - + "" + ":" + "from " - + "" + ":" + "from " - + "" + "()>:" + "to ()>" + "from " - + "" + "()>:" + "to ()>" + "from ()>" - + "" + ":" + "from " - + "" + "()>:" + "to ()>" + "from " - + "" + ":" + "from " - + "" + ":" + "to ()>" + "to " @@ -224,10 +215,8 @@ public void testMiscExample1() { + "to ()>" + "to " + "to ()>" - + "" + "()>:" + "from " - + "" + "()>:" + "from ()>"); } From a75d56e521f983fe27253086161e545e4078bdae Mon Sep 17 00:00:00 2001 From: Jonas Klauke Date: Fri, 17 Nov 2023 14:52:25 +0100 Subject: [PATCH 21/23] added testcase for incomplete concreteDispatch in which the concrete method is in the missing class. Changed the test to use the bytecode frontend --- .../callgraph/AbstractCallGraphAlgorithm.java | 12 ++++++++++- .../callgraph/ConcreteDispatchTest.java | 20 +++++++++++------- .../ConcreteCallSuperClassIncomplete.java | 17 +++++++++++++++ .../callgraph/ConcreteDispatch/binary/A.class | Bin 0 -> 225 bytes .../callgraph/ConcreteDispatch/binary/B.class | Bin 0 -> 285 bytes .../callgraph/ConcreteDispatch/binary/C.class | Bin 0 -> 185 bytes .../callgraph/ConcreteDispatch/binary/D.class | Bin 0 -> 170 bytes .../callgraph/ConcreteDispatch/binary/I.class | Bin 0 -> 186 bytes .../callgraph/ConcreteDispatch/binary/J.class | Bin 0 -> 246 bytes .../binary/cvcscincomplete/Class.class | Bin 0 -> 352 bytes 10 files changed, 40 insertions(+), 9 deletions(-) create mode 100644 sootup.callgraph/src/test/resources/callgraph/ConcreteDispatch/ConcreteCallSuperClassIncomplete.java create mode 100644 sootup.callgraph/src/test/resources/callgraph/ConcreteDispatch/binary/A.class create mode 100644 sootup.callgraph/src/test/resources/callgraph/ConcreteDispatch/binary/B.class create mode 100644 sootup.callgraph/src/test/resources/callgraph/ConcreteDispatch/binary/C.class create mode 100644 sootup.callgraph/src/test/resources/callgraph/ConcreteDispatch/binary/D.class create mode 100644 sootup.callgraph/src/test/resources/callgraph/ConcreteDispatch/binary/I.class create mode 100644 sootup.callgraph/src/test/resources/callgraph/ConcreteDispatch/binary/J.class create mode 100644 sootup.callgraph/src/test/resources/callgraph/ConcreteDispatch/binary/cvcscincomplete/Class.class diff --git a/sootup.callgraph/src/main/java/sootup/callgraph/AbstractCallGraphAlgorithm.java b/sootup.callgraph/src/main/java/sootup/callgraph/AbstractCallGraphAlgorithm.java index c2d796bce61..15aec6c97e8 100644 --- a/sootup.callgraph/src/main/java/sootup/callgraph/AbstractCallGraphAlgorithm.java +++ b/sootup.callgraph/src/main/java/sootup/callgraph/AbstractCallGraphAlgorithm.java @@ -494,7 +494,17 @@ public static Optional resolveConcreteDispatch( public static Optional findConcreteMethod( @Nonnull View> view, @Nonnull MethodSignature sig) { IdentifierFactory identifierFactory = view.getIdentifierFactory(); - Optional startMethod = view.getMethod(sig); + SootClass startclass = view.getClass(sig.getDeclClassType()).orElse(null); + if (startclass == null) { + logger.warn( + "Could not find \"" + + sig.getDeclClassType() + + "\" of method" + + sig + + " to resolve the concrete method"); + return Optional.empty(); + } + Optional startMethod = startclass.getMethod(sig.getSubSignature()); if (startMethod.isPresent()) { return startMethod; } diff --git a/sootup.callgraph/src/test/java/sootup/callgraph/ConcreteDispatchTest.java b/sootup.callgraph/src/test/java/sootup/callgraph/ConcreteDispatchTest.java index 5bb68192ae1..d8d8ce95836 100644 --- a/sootup.callgraph/src/test/java/sootup/callgraph/ConcreteDispatchTest.java +++ b/sootup.callgraph/src/test/java/sootup/callgraph/ConcreteDispatchTest.java @@ -18,7 +18,6 @@ import sootup.java.core.JavaProject; import sootup.java.core.language.JavaLanguage; import sootup.java.core.views.JavaView; -import sootup.java.sourcecode.inputlocation.JavaSourcePathAnalysisInputLocation; /** @author : Hasitha Rajapakse, Jonas Klauke * */ @Category(Java8Test.class) @@ -34,8 +33,8 @@ public static void setUp() { JavaProject project = JavaProject.builder(new JavaLanguage(8)) .addInputLocation( - new JavaSourcePathAnalysisInputLocation( - Collections.singleton("src/test/resources/callgraph/ConcreteDispatch/"))) + new JavaClassPathAnalysisInputLocation( + "src/test/resources/callgraph/ConcreteDispatch/binary")) .addInputLocation( new JavaClassPathAnalysisInputLocation( System.getProperty("java.home") + "/lib/rt.jar")) @@ -46,20 +45,25 @@ public static void setUp() { @Test public void invalidResolveConcreteDispatch() { IdentifierFactory factory = view.getIdentifierFactory(); - Optional ms = + Optional incompleteMethod = AbstractCallGraphAlgorithm.resolveConcreteDispatch( - view, factory.parseMethodSignature("java.util.Collection#size(): int")); - assertFalse(ms.isPresent()); + view, factory.parseMethodSignature("cvcscincomplete.Class#target(): void")); + assertFalse(incompleteMethod.isPresent()); } @Test() public void invalidResolveConcreteDispatchOfAbstractMethod() { IdentifierFactory factory = view.getIdentifierFactory(); - Optional ms = + Optional abstractMethod = AbstractCallGraphAlgorithm.resolveConcreteDispatch( view, factory.parseMethodSignature("java.util.AbstractList#get(int): java.lang.Object")); - assertFalse(ms.isPresent()); + assertFalse(abstractMethod.isPresent()); + + Optional interfaceMethod = + AbstractCallGraphAlgorithm.resolveConcreteDispatch( + view, factory.parseMethodSignature("java.util.Collection#size(): int")); + assertFalse(interfaceMethod.isPresent()); } @Test diff --git a/sootup.callgraph/src/test/resources/callgraph/ConcreteDispatch/ConcreteCallSuperClassIncomplete.java b/sootup.callgraph/src/test/resources/callgraph/ConcreteDispatch/ConcreteCallSuperClassIncomplete.java new file mode 100644 index 00000000000..a46ececd498 --- /dev/null +++ b/sootup.callgraph/src/test/resources/callgraph/ConcreteDispatch/ConcreteCallSuperClassIncomplete.java @@ -0,0 +1,17 @@ +// cvc/Class.java +package cvcscincomplete; + +class Class extends SuperClass{ + + public static void main(String[] args){ + Class cls = new Class(); + cls.target(); + } +} + +//class file of the superclass is not in the inputlocation +class SuperClass { + + public void target(){ } + +} diff --git a/sootup.callgraph/src/test/resources/callgraph/ConcreteDispatch/binary/A.class b/sootup.callgraph/src/test/resources/callgraph/ConcreteDispatch/binary/A.class new file mode 100644 index 0000000000000000000000000000000000000000..2c6b3cd9c77e40622e42765c48b1aaf007fbca2d GIT binary patch literal 225 zcmYLC!4APt5S(qbkEgUhAmN6ClVc+(i9{kU#J%MuDzu5H|8kNz_y8Xzraf?&o!yzq z?ECY40T?6Du;D4R6xs^Dzzx$Zy-oynf3OwQX8GO-{AHS%RdG(tWfLbSD(=}_5BXl8 z*LiW-nR!~N(dZc8Vh;{nfkecVSfB@skJ!hr7MVV$;?*Gw*%>|{cP@fDXK90RLE{(g W^0G=b(BVI+E6ruSqOa*&Xnp~jM;kf- literal 0 HcmV?d00001 diff --git a/sootup.callgraph/src/test/resources/callgraph/ConcreteDispatch/binary/B.class b/sootup.callgraph/src/test/resources/callgraph/ConcreteDispatch/binary/B.class new file mode 100644 index 0000000000000000000000000000000000000000..dc3f1fe55abd570b38ebec21d672c720dd42eede GIT binary patch literal 285 zcma(~%MQU%6r7{&ZPoJ!MC`C25lgWku@V-oYxul3T?czV-?4-o7%fHCpb`T_! literal 0 HcmV?d00001 diff --git a/sootup.callgraph/src/test/resources/callgraph/ConcreteDispatch/binary/C.class b/sootup.callgraph/src/test/resources/callgraph/ConcreteDispatch/binary/C.class new file mode 100644 index 0000000000000000000000000000000000000000..11c37bfe1750f1b048015851853b1fd42052fe1b GIT binary patch literal 185 zcmX^0Z`VEs1_l!bel7-P25xo+9(D#^b_PC11~!|_yv!0iMh0dL%`ip=7U%qwR7M7V zpUk{eztY^K)S{5Yq#U3KS8#r5QF5wVCWyo4te2HomdL}v%D~3R!061#AOPa)=OpH( z>-#5Vr6!jEg*_P*8JK|90Zj$k4HN(=2a@bSo-CNpz`&}toq=&9SegS!f}|lzIe|12 G0~Y`|KOF`D literal 0 HcmV?d00001 diff --git a/sootup.callgraph/src/test/resources/callgraph/ConcreteDispatch/binary/D.class b/sootup.callgraph/src/test/resources/callgraph/ConcreteDispatch/binary/D.class new file mode 100644 index 0000000000000000000000000000000000000000..bf0e309a8b8e61e67e3b6667d8367d9909de24df GIT binary patch literal 170 zcmX^0Z`VEs1_l!bel7-P25xo+9(D#^b_PC11~!|_yv!0iMh0dL%`ip=7U%qwR7M7V zpUk{eztY^K)S{5Yq#U3KS8#r5QF5wVCWyo4qL-CemdL}v%D~3Rz~}=HCvx#yx{;6!LST<=J zbhyaO(#ZhW@oc_sy#trjOjD{wwe{-NjUMTDl}l>QJvJ8l Ee_df97ytkO literal 0 HcmV?d00001 diff --git a/sootup.callgraph/src/test/resources/callgraph/ConcreteDispatch/binary/J.class b/sootup.callgraph/src/test/resources/callgraph/ConcreteDispatch/binary/J.class new file mode 100644 index 0000000000000000000000000000000000000000..cbb7535064d2e10ed5c54bf1b479353fddcba6f8 GIT binary patch literal 246 zcmX^0Z`VEs1_l!b9(D#Ub_Q-n29e^@g480Uo0nczVp$?11EUusg8+!B zpOcuEuJ50em6}|_1{7yt0y={MNCK^5U;&b>K%N3roJD}cW+RXfGzds?fz`4C)v*CZ Z*x{<>p{kkLF%)wEwQvHZIlxwM0swnnDNg_Z literal 0 HcmV?d00001 diff --git a/sootup.callgraph/src/test/resources/callgraph/ConcreteDispatch/binary/cvcscincomplete/Class.class b/sootup.callgraph/src/test/resources/callgraph/ConcreteDispatch/binary/cvcscincomplete/Class.class new file mode 100644 index 0000000000000000000000000000000000000000..7f60d2089b09cb2a930b052286bf890b7b7c51c5 GIT binary patch literal 352 zcmZ8c%SyvQ6g|@Bq2r-dPv+DhuC50Un!&7YX)y}`oQ34^|NFc<;uvr zu9{1G&*z#Lzv9X;988|_7rx}F=B7-G)+$rZTreu?&I);>ocScHjj*zn8P~e#7Sd*# zH_fdPb+ypMPhCfhK0=0lu@sF^e_Fcs#@Kb@;eT<{Mt^U)EoF;ix+b72K?qk0h5>Cy zgp4paTY Date: Fri, 17 Nov 2023 14:55:20 +0100 Subject: [PATCH 22/23] removed unnecessary method in AbstractCallGraphAlgorithm --- .../callgraph/AbstractCallGraphAlgorithm.java | 42 +------------------ 1 file changed, 1 insertion(+), 41 deletions(-) diff --git a/sootup.callgraph/src/main/java/sootup/callgraph/AbstractCallGraphAlgorithm.java b/sootup.callgraph/src/main/java/sootup/callgraph/AbstractCallGraphAlgorithm.java index 15aec6c97e8..2fddb264117 100644 --- a/sootup.callgraph/src/main/java/sootup/callgraph/AbstractCallGraphAlgorithm.java +++ b/sootup.callgraph/src/main/java/sootup/callgraph/AbstractCallGraphAlgorithm.java @@ -282,46 +282,6 @@ protected Stream resolveAllStaticInitializerCallsFromSourceMeth .map(SootClassMember::getSignature); } - /** - * searches the method object in the given hierarchy - * - * @param view it contains all classes - * @param sig the signature of the searched method - * @param the generic type of the searched method object - * @return the found method object, or null if the method was not found. - */ - protected final T findMethodInHierarchy( - @Nonnull View> view, @Nonnull MethodSignature sig) { - Optional> optSc = view.getClass(sig.getDeclClassType()); - - if (optSc.isPresent()) { - SootClass sc = optSc.get(); - - List superClasses = view.getTypeHierarchy().superClassesOf(sc.getType()); - Set interfaces = view.getTypeHierarchy().implementedInterfacesOf(sc.getType()); - superClasses.addAll(interfaces); - - for (ClassType superClassType : superClasses) { - Optional> superClassOpt = view.getClass(superClassType); - if (superClassOpt.isPresent()) { - SootClass superClass = superClassOpt.get(); - Optional methodOpt = superClass.getMethod(sig.getSubSignature()); - if (methodOpt.isPresent()) { - return (T) methodOpt.get(); - } - } - } - logger.warn( - "Could not find \"" - + sig.getSubSignature() - + "\" in " - + sig.getDeclClassType().getClassName() - + " and in its superclasses"); - } else { - logger.trace("Could not find \"" + sig.getDeclClassType() + "\" in view"); - } - return null; - } /** * This method enables optional pre-processing of a method in the call graph algorithm @@ -444,7 +404,7 @@ public MethodSignature findMainMethod() { "There are more than 1 main method present.\n Below main methods are found: \n" + mainMethods + "\n initialize() method can be used if only one main method exists. \n You can specify these main methods as entry points by passing them as parameter to initialize method."); - } else if (mainMethods.size() == 0) { + } else if (mainMethods.isEmpty()) { throw new RuntimeException( "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."); } From 0ddd798e7d804347c35476aa2d9d4bc7851feafd Mon Sep 17 00:00:00 2001 From: Jonas Klauke Date: Fri, 17 Nov 2023 15:01:01 +0100 Subject: [PATCH 23/23] fmt --- .../main/java/sootup/callgraph/AbstractCallGraphAlgorithm.java | 1 - 1 file changed, 1 deletion(-) diff --git a/sootup.callgraph/src/main/java/sootup/callgraph/AbstractCallGraphAlgorithm.java b/sootup.callgraph/src/main/java/sootup/callgraph/AbstractCallGraphAlgorithm.java index 2fddb264117..7ab1607c983 100644 --- a/sootup.callgraph/src/main/java/sootup/callgraph/AbstractCallGraphAlgorithm.java +++ b/sootup.callgraph/src/main/java/sootup/callgraph/AbstractCallGraphAlgorithm.java @@ -282,7 +282,6 @@ protected Stream resolveAllStaticInitializerCallsFromSourceMeth .map(SootClassMember::getSignature); } - /** * This method enables optional pre-processing of a method in the call graph algorithm *