Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
cde2e34
added MethodHandle test cases
moebarni Jun 22, 2025
732834e
added preanalysis in AbstractCallGraphAlgorithm constructor; added ch…
moebarni Jun 25, 2025
fa59d4a
updated test cases
moebarni Jun 25, 2025
4436c41
added VarHandle test
moebarni Jun 30, 2025
f24ff9a
bad "solution" for RTA
moebarni Jun 30, 2025
eed2df1
Merge branch 'develop' into 1266-feature-investigate-varargs-cg
moebarni Jun 30, 2025
ece4faa
wrapper approach (failed to detect new MethodHandle instance)
moebarni Jul 7, 2025
9c20cf2
extended collectInstantiatedClassesInMethod (only for method/varHandl…
moebarni Jul 11, 2025
9eb4cb8
with comments and separated version
moebarni Jul 11, 2025
5a95bdf
cleaned
moebarni Jul 11, 2025
3aeebd4
Merge branch 'develop' into 1266-feature-investigate-varargs-cg
moebarni Jul 11, 2025
1646566
deleted sout, comment, first test
moebarni Jul 29, 2025
d2a4f11
changed method name; deleted test case for example1; deleted unnecess…
moebarni Jul 29, 2025
1d450b4
deleted sout; added diffParamTest + superClassTest
moebarni Jul 29, 2025
2348c11
Merge branch 'develop' into 1266-feature-investigate-varargs-cg
moebarni Jul 30, 2025
07de6b4
fixed format
moebarni Jul 30, 2025
706fc81
changed preanalysis data structure to Table
moebarni Jul 30, 2025
bc8763d
formatting
moebarni Jul 30, 2025
2988813
Merge branch 'develop' into 1266-feature-investigate-varargs-cg
moebarni Jul 30, 2025
19397b2
deleted duplicate getDeclClassType-method; add methodName-subtype pai…
moebarni Jul 31, 2025
5a00c10
updated methodName-subType pairs
moebarni Aug 1, 2025
5261278
rollback collectInstantiatedClassesInMethod
moebarni Aug 3, 2025
ba19f9b
fmt
moebarni Aug 3, 2025
a8aa9d6
structural changes (getMethodsWithPolymorphicAnnotation)
moebarni Aug 4, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion sootup.callgraph/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@
<artifactId>sootup.jimple.frontend</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependency>
<groupId>org.soot-oss</groupId>
<artifactId>sootup.java.core</artifactId>
</dependency>
</dependencies>

</project>
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@

import static sootup.core.jimple.basic.StmtPositionInfo.getNoStmtPositionInfo;

import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Table;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
Expand All @@ -49,6 +51,8 @@
import sootup.core.types.ClassType;
import sootup.core.types.VoidType;
import sootup.core.views.View;
import sootup.java.core.AnnotationUsage;
import sootup.java.core.JavaSootClass;

/**
* The AbstractCallGraphAlgorithm class is the super class of all call graph algorithm. It provides
Expand All @@ -61,10 +65,51 @@ public abstract class AbstractCallGraphAlgorithm implements CallGraphAlgorithm {

@NonNull protected final View view;
@NonNull protected final TypeHierarchy typeHierarchy;
@NonNull protected final Table<String, String, SootMethod> preanalysis;

protected AbstractCallGraphAlgorithm(@NonNull View view) {
this.view = view;
this.typeHierarchy = view.getTypeHierarchy();
this.preanalysis = getMethodsWithPolymorphicAnnotation();
}

/**
* Collects all method signatures from the current view that are annotated with {@code
* java.lang.invoke.MethodHandle$PolymorphicSignature}.
*
* @return a list of method signatures annotated with {@code @PolymorphicSignature}
*/
protected Table<String, String, SootMethod> getMethodsWithPolymorphicAnnotation() {
Table<String, String, SootMethod> polymorphicMethods = HashBasedTable.create();
ClassType polymorphicAnnotationType =
view.getIdentifierFactory()
.getClassType("java.lang.invoke.MethodHandle$PolymorphicSignature");
view.getClasses()
.filter(sootClass -> sootClass instanceof JavaSootClass)
.map(sootClass -> (JavaSootClass) sootClass)
.flatMap(javaSootClass -> javaSootClass.getMethods().stream())
.filter(
javaSootMethod -> {
for (AnnotationUsage annotationUsage : javaSootMethod.getAnnotations()) {
if (annotationUsage.getAnnotation().equals(polymorphicAnnotationType)) {
return true;
}
}
return false;
})
.forEach(
javaSootMethod -> {
ClassType classType = javaSootMethod.getDeclaringClassType();
String methodName = javaSootMethod.getName();
polymorphicMethods.put(methodName, classType.getFullyQualifiedName(), javaSootMethod);
typeHierarchy
.subtypesOf(classType)
.forEach(
type ->
polymorphicMethods.put(
methodName, type.getFullyQualifiedName(), javaSootMethod));
});
return polymorphicMethods;
}

/**
Expand Down Expand Up @@ -592,18 +637,7 @@ protected static Optional<? extends SootMethod> findConcreteMethod(
}

// search method in interfaces
Optional<? extends SootMethod> defaultMethod = findDefaultMethod(view, startClass, methodSig);
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();
return findDefaultMethod(view, startClass, methodSig);
}

protected static Optional<SootMethod> findMethodInHierarchy(
Expand Down Expand Up @@ -667,4 +701,30 @@ protected static Optional<? extends SootMethod> findDefaultMethod(
protected boolean isInterface(ClassType classType) {
return typeHierarchy.isInterface(classType);
}

/**
* Attempts to resolve a previously unresolved method signature by comparing it with all known
* method signatures annotated with {@code @PolymorphicSignature}. If a match is found (ignoring
* the parameters), the corresponding SootMethod is returned.
*
* @param targetMethodSignature the signature of the searched method
* @return the found method object, or null if the method was not found.
*/
protected Optional<? extends SootMethod> findMatchingVarArgsMethod(
@NonNull MethodSignature targetMethodSignature) {
Optional<SootMethod> resolvedVarArgsMethod =
Optional.ofNullable(
preanalysis.get(
targetMethodSignature.getName(),
targetMethodSignature.getDeclClassType().getFullyQualifiedName()));
if (resolvedVarArgsMethod.isEmpty()) {
logger.warn(
"Could not find \""
+ targetMethodSignature.getSubSignature()
+ "\" in "
+ targetMethodSignature.getDeclClassType().getClassName()
+ " and in its superclasses and interfaces");
}
return resolvedVarArgsMethod;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,14 @@ protected Stream<MethodSignature> resolveCall(SootMethod method, InvokableStmt i
if (actualTargetMethod == null) {
// method not implemented, search for implementation in super classes or ínterfaces
actualTargetMethod = findConcreteMethod(view, targetMethodSignature).orElse(null);
// method implementation isn't contained in the view. return the called method as target
if (actualTargetMethod == null) {
return Stream.of(targetMethodSignature);
// method implementation isn't contained in the view.
// check if method got the PolymorphicSignature annotation
actualTargetMethod = findMatchingVarArgsMethod(targetMethodSignature).orElse(null);
if (actualTargetMethod == null) {
// method couldn't be resolved, return the called method as target
return Stream.of(targetMethodSignature);
}
}
}

Expand Down Expand Up @@ -126,7 +131,7 @@ protected Stream<MethodSignature> resolveCall(SootMethod method, InvokableStmt i
targets,
resolveAllDefaultTargets(
subclasses,
actualTargetMethod.getDeclClassType(),
actualTargetMethod.getDeclaringClassType(),
targetMethodSignature.getSubSignature()));
// all subtypes that do not have an implementation of the method
// can have an implementation in the supertype
Expand Down Expand Up @@ -166,7 +171,7 @@ private Stream<MethodSignature> resolveAllDefaultTargets(
view.getIdentifierFactory()
.getMethodSignature(classType, targetSubSignature))
.stream())
.filter(sootMethod -> interfaces.contains(sootMethod.getDeclClassType()))
.filter(sootMethod -> interfaces.contains(sootMethod.getDeclaringClassType()))
.map(SootMethod::getSignature);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,9 +141,14 @@ protected Stream<MethodSignature> resolveCall(
if (actualTargetMethod == null) {
// method not implemented, search for implementation in super classes or ínterfaces
actualTargetMethod = findConcreteMethod(view, targetMethodSignature).orElse(null);
// method implementation isn't contained in the view. return the called method as target
if (actualTargetMethod == null) {
return Stream.of(targetMethodSignature);
// method implementation isn't contained in the view.
// check if method got the PolymorphicSignature annotation
actualTargetMethod = findMatchingVarArgsMethod(targetMethodSignature).orElse(null);
if (actualTargetMethod == null) {
// method couldn't be resolved, return the called method as target
return Stream.of(targetMethodSignature);
}
}
}
// special invokes and static invokes can only have one specific target
Expand All @@ -159,7 +164,6 @@ protected Stream<MethodSignature> resolveCall(
.subtypesOf(targetMethodSignature.getDeclClassType())
.flatMap(classType -> view.getClass(classType).stream())
.toList();

// get all targets of these subtypes
Stream<MethodSignature> targets =
resolveAllCallTargets(
Expand Down Expand Up @@ -317,7 +321,6 @@ protected void preProcessingMethod(
if (method == null) {
return;
}

List<ClassType> newInstantiatedClasses = collectInstantiatedClassesInMethod(method);
newInstantiatedClasses.forEach(
classType -> includeIgnoredCallsToClass(classType, cg, workList));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@

import static org.junit.jupiter.api.Assertions.*;

import java.util.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import org.junit.jupiter.api.Test;
import sootup.core.inputlocation.AnalysisInputLocation;
import sootup.core.jimple.common.Value;
Expand Down Expand Up @@ -1265,4 +1266,56 @@ public void testImplicitExample6() {
identifierFactory.getClassType("t6.NoThread"), "run", "void", Collections.emptyList());
assertFalse(cg.containsMethod(runMethodSig));
}

@Test
public void testMethodHandleDiffParamExample1() {
CallGraph cg = loadCallGraph("Polymorphic", "e1.MethodHandleDiffParamExample1");
MethodSignature invokeMethodSig =
identifierFactory.getMethodSignature(
identifierFactory.getClassType("java.lang.invoke.MethodHandle"),
"invoke",
"java.lang.Object",
Collections.singletonList("java.lang.Object[]"));
Set<MethodSignature> callSourcesMethodSigs = cg.callSourcesTo(invokeMethodSig);
assertTrue(callSourcesMethodSigs.contains(mainMethodSignature));
}

@Test
public void testMethodHandleInvokeExample2() {
CallGraph cg = loadCallGraph("Polymorphic", "e2.MethodHandleInvokeExample2");
MethodSignature invokeMethodSig =
identifierFactory.getMethodSignature(
identifierFactory.getClassType("java.lang.invoke.MethodHandle"),
"invoke",
"java.lang.Object",
Collections.singletonList("java.lang.Object[]"));
Set<MethodSignature> callSourcesMethodSigs = cg.callSourcesTo(invokeMethodSig);
assertTrue(callSourcesMethodSigs.contains(mainMethodSignature));
}

@Test
public void testVarHandleGetExample3() {
CallGraph cg = loadCallGraph("Polymorphic", "e3.VarHandleGetExample3");
MethodSignature getMethodSig =
identifierFactory.getMethodSignature(
identifierFactory.getClassType("java.lang.invoke.VarHandle"),
"get",
"java.lang.Object",
Collections.singletonList("java.lang.Object[]"));
Set<MethodSignature> callSourcesMethodSigs = cg.callSourcesTo(getMethodSig);
assertTrue(callSourcesMethodSigs.contains(mainMethodSignature));
}

@Test
public void testMethodHandleSuperClassExample4() {
CallGraph cg = loadCallGraph("Polymorphic", "e4.MethodHandleSuperClassExample4");
MethodSignature invokeMethodSig =
identifierFactory.getMethodSignature(
identifierFactory.getClassType("java.lang.invoke.MethodHandle"),
"invoke",
"java.lang.Object",
Collections.singletonList("java.lang.Object[]"));
Set<MethodSignature> callSourcesMethodSigs = cg.callSourcesTo(invokeMethodSig);
assertTrue(callSourcesMethodSigs.contains(mainMethodSignature));
}
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package e1;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;

public class MethodHandleDiffParamExample1 {
public static void main(String[] args) throws Throwable {
MethodHandle handle = MethodHandles.lookup()
.findVirtual(
String.class,
"indexOf",
MethodType.methodType(int.class, String.class, int.class)
);

handle.invoke("HelloWorld", "lo", 3);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package e2;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;

public class MethodHandleInvokeExample2 {
public static void main(String[] args) throws Throwable {
MethodHandle handle = MethodHandles.lookup()
.findVirtual(String.class, "substring", MethodType.methodType(String.class, int.class, int.class));

handle.invoke("HelloWorld", 0, 5);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package e4;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;

public class MethodHandleSuperClassExample4 {
static class Base {
protected void method1(String dummy) {
System.out.println("Method1: " + dummy);
}
}

static class Derived extends Base {}

public static void main(String[] args) throws Throwable {
MethodHandle handle = MethodHandles.lookup()
.findVirtual(Base.class, "method1", MethodType.methodType(void.class, String.class));

handle.invoke(new Derived(), "SubClass!");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package e3;

import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;

public class VarHandleGetExample3 {
static int value = 42;

public static void main(String[] args) throws Throwable {
VarHandle handle = MethodHandles.lookup()
.findStaticVarHandle(VarHandleGetExample3.class, "value", int.class);

handle.get();
}
}
2 changes: 0 additions & 2 deletions sootup.core/src/main/java/sootup/core/model/SootMethod.java
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,6 @@ public interface SootMethod extends Method {

@NonNull MethodSubSignature getSubSignature();

@NonNull ClassType getDeclClassType();

@NonNull String getName();

@NonNull List<ClassType> getExceptionSignatures();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,11 +176,6 @@ public List<Type> getParameterTypes() {
return getSignature().getParameterTypes();
}

@NonNull
public ClassType getDeclClassType() {
return getSignature().getDeclClassType();
}

/** Returns the SootClass declaring this one. */
@NonNull
@Override
Expand Down
Loading