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 16c604ceb09..ff6f94fb36d 100644 --- a/sootup.core/src/main/java/sootup/core/typehierarchy/MethodDispatchResolver.java +++ b/sootup.core/src/main/java/sootup/core/typehierarchy/MethodDispatchResolver.java @@ -27,6 +27,7 @@ import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; +import java.util.stream.Stream; import javax.annotation.Nonnull; import sootup.core.frontend.ResolveException; import sootup.core.jimple.common.expr.JSpecialInvokeExpr; @@ -85,7 +86,7 @@ public static Set resolveAbstractDispatch( () -> new ResolveException( "Could not resolve " + subtype + ", but found it in hierarchy."))) - .map(sootClass -> findConcreteMethodInSootClass(sootClass, m)) + .map(sootClass -> sootClass.getMethod(m.getSubSignature())) .filter(Optional::isPresent) .map(Optional::get) .filter(method -> !method.isAbstract()) @@ -111,7 +112,7 @@ public static Set resolveAllDispatchesInClasses( new ResolveException( "Could not resolve " + subtype + ", but found it in hierarchy."))) .filter(c -> classes.contains(c.getType())) - .map(sootClass -> findConcreteMethodInSootClass(sootClass, m)) + .map(sootClass -> sootClass.getMethod(m.getSubSignature())) .filter(Optional::isPresent) .map(Optional::get) .filter(method -> !method.isAbstract()) @@ -172,6 +173,20 @@ 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 @@ -181,45 +196,33 @@ public static boolean canDispatch( public static Optional resolveConcreteDispatch( View> view, MethodSignature m) { TypeHierarchy hierarchy = view.getTypeHierarchy(); - ClassType superClassType = m.getDeclClassType(); - SootClass startClass = view.getClass(superClassType).orElse(null); - ArrayList> classesInHierachyOrder = new ArrayList<>(); + ClassType current = m.getDeclClassType(); + SootClass startClass = view.getClass(current).orElse(null); + List> classesInHierarchyOrder = findSuperClassesInclusive(view, current); - // search concrete method in the class itself and its super classes - do { - ClassType finalSuperClassType = superClassType; - SootClass superClass = - view.getClass(superClassType) - .orElseThrow( - () -> - new ResolveException( - "Did not find class " + finalSuperClassType + " in View")); - - classesInHierachyOrder.add(superClass); - - SootMethod concreteMethod = findConcreteMethodInSootClass(superClass, m).orElse(null); - if (concreteMethod != null && !concreteMethod.isAbstract()) { - // found method is not abstract - return Optional.of(concreteMethod.getSignature()); - } - if (concreteMethod != null && concreteMethod.isAbstract()) { - if (startClass.isAbstract() - && !startClass.getType().equals(concreteMethod.getDeclaringClassType())) { - // A not implemented method of an abstract class results into an abstract method - return Optional.empty(); + 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"); } - // 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"); } - - superClassType = hierarchy.superClassOf(superClassType); - } while (superClassType != null); + } // No super class contains the implemented method, search the concrete method in interfaces // first collect all interfaces and super interfaces List> worklist = - classesInHierachyOrder.stream() + classesInHierarchyOrder.stream() .flatMap(sootClass -> getSootClassesOfInterfaces(view, sootClass).stream()) .collect(Collectors.toList()); ArrayList> processedInterface = new ArrayList<>(); @@ -233,7 +236,7 @@ public static Optional resolveConcreteDispatch( // add found default method to possibleDefaultMethods Optional concreteMethod = - findConcreteMethodInSootClass(currentInterface, m); + currentInterface.getMethod(m.getSubSignature()); concreteMethod.ifPresent(possibleDefaultMethods::add); // if no default message is found search the default message in super interfaces @@ -282,29 +285,6 @@ private static List> getSootClassesOfInterfaces( .collect(Collectors.toList()); } - /** - * finds the concrete method in a SootClass - * - *

this method returns the concrete method of given method signature in a SootClass. Due to - * covariant, the given method signature can differ from the concrete method at the return type - * The method goes through all methods of the given SootClass and searches for a method which can - * dispatch. - * - * @param sootClass The method is searched in this SootClass - * @param methodSignature the signature of the searched method - * @return an Optional Object that can contain the found concrete method in the given SootClass - */ - private static Optional findConcreteMethodInSootClass( - SootClass sootClass, MethodSignature methodSignature) { - return sootClass.getMethods().stream() - .filter( - potentialTarget -> - methodSignature - .getSubSignature() - .equals(potentialTarget.getSignature().getSubSignature())) - .findAny(); - } - /** * Resolves the actual method called by the specialInvokeExpr that is contained by * container.