Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

use cached values i.e. for the typehierarchy #658

Merged
merged 11 commits into from
Aug 1, 2023
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ default boolean isSubtype(@Nonnull Type supertype, @Nonnull Type potentialSubtyp

/**
* Returns all superclasses of <code>classType</code> up to <code>java.lang.Object</code>, which
* will be the last entry in the list.
* will be the last entry in the list. i.e. its ordered from bottom level to top level.
*/
@Nonnull
default List<ClassType> superClassesOf(@Nonnull ClassType classType) {
Expand Down Expand Up @@ -207,4 +207,10 @@ default List<ClassType> incompleteSuperClassesOf(@Nonnull ClassType classType) {
}
return superClasses;
}

Set<ClassType> directlyImplementedInterfacesOf(@Nonnull ClassType type);

boolean isInterface(@Nonnull ClassType type);

Set<ClassType> directlyExtendedInterfacesOf(@Nonnull ClassType type);
}
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,6 @@ public Stream<Vertex> directlyExtendedInterfacesOf(@Nonnull Vertex interfaceVert
.map(graph::getEdgeTarget);
}

/** for completeness - You could use Sootclass.getSuperclass() directly */
public Stream<Vertex> directSuperClassOf(@Nonnull Vertex classVertex) {
Graph<Vertex, Edge> graph = lazyScanResult.get().graph;
return graph.outgoingEdgesOf(classVertex).stream()
Expand All @@ -192,7 +191,7 @@ public Stream<Vertex> directSuperClassOf(@Nonnull Vertex classVertex) {
public Set<ClassType> directlyImplementedInterfacesOf(@Nonnull ClassType classType) {
Vertex vertex = lazyScanResult.get().typeToVertex.get(classType);
if (vertex == null) {
throw new RuntimeException("Could not find " + classType + " in hierarchy.");
throw new IllegalStateException("Could not find '" + classType + "' in hierarchy.");
}
if (vertex.type != VertexType.Class) {
throw new IllegalArgumentException(classType + " is not a class.");
Expand All @@ -206,7 +205,7 @@ public Set<ClassType> directlyImplementedInterfacesOf(@Nonnull ClassType classTy
public Set<ClassType> directlyExtendedInterfacesOf(@Nonnull ClassType interfaceType) {
Vertex vertex = lazyScanResult.get().typeToVertex.get(interfaceType);
if (vertex == null) {
throw new RuntimeException("Could not find " + interfaceType + " in hierarchy.");
throw new IllegalStateException("Could not find " + interfaceType + " in hierarchy.");
}
if (vertex.type != VertexType.Interface) {
throw new IllegalArgumentException(interfaceType + " is not a class.");
Expand All @@ -216,11 +215,16 @@ public Set<ClassType> directlyExtendedInterfacesOf(@Nonnull ClassType interfaceT
.collect(Collectors.toSet());
}

/**
* method exists for completeness - superClassOf() / which is basically SootClass.getSuperClass()
* should be more performant.
*/
@Nullable
@Deprecated
public ClassType directSuperClassOf(@Nonnull ClassType classType) {
Vertex vertex = lazyScanResult.get().typeToVertex.get(classType);
if (vertex == null) {
throw new RuntimeException("Could not find " + classType + " in hierarchy.");
throw new IllegalStateException("Could not find " + classType + " in hierarchy.");
}
Graph<Vertex, Edge> graph = lazyScanResult.get().graph;
List<Vertex> list =
Expand All @@ -230,6 +234,7 @@ public ClassType directSuperClassOf(@Nonnull ClassType classType) {
.collect(Collectors.toList());

if (list.isEmpty()) {
/* is java.lang.Object */
return null;
} else if (list.size() > 1) {
throw new RuntimeException(classType + "cannot have multiple superclasses");
Expand All @@ -245,7 +250,7 @@ public Set<ClassType> implementedInterfacesOf(@Nonnull ClassType type) {
Vertex vertex = scanResult.typeToVertex.get(type);

if (vertex == null) {
throw new ResolveException("Could not find " + type + " in hierarchy for view " + view);
throw new IllegalStateException("Could not find " + type + " in hierarchy for view " + view);
}

switch (vertex.type) {
Expand Down
3 changes: 2 additions & 1 deletion sootup.core/src/main/java/sootup/core/types/Type.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
* #L%
*/

import javax.annotation.Nonnull;
import sootup.core.jimple.visitor.Acceptor;
import sootup.core.jimple.visitor.TypeVisitor;

Expand Down Expand Up @@ -50,7 +51,7 @@ public static boolean isObject(Type type) {
* This method is used to make an array type for the given type. If the given type is an array
* type, then increase its dimension with given dim
*/
public static ArrayType makeArrayType(Type type, int dim) {
public static ArrayType createArrayType(@Nonnull Type type, int dim) {
if (type instanceof ArrayType) {
return new ArrayType(
((ArrayType) type).getBaseType(), ((ArrayType) type).getDimension() + dim);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
* #L%
*/

import com.google.common.collect.ImmutableSet;
import java.util.*;
import javax.annotation.Nonnull;
import sootup.core.IdentifierFactory;
Expand All @@ -33,24 +34,42 @@
import sootup.core.jimple.common.ref.*;
import sootup.core.jimple.common.stmt.Stmt;
import sootup.core.model.SootClass;
import sootup.core.typehierarchy.ViewTypeHierarchy;
import sootup.core.types.ArrayType;
import sootup.core.types.ClassType;
import sootup.core.types.PrimitiveType;
import sootup.core.types.Type;
import sootup.core.views.View;
import sootup.java.bytecode.interceptors.typeresolving.types.AugIntegerTypes;
import sootup.java.bytecode.interceptors.typeresolving.types.BottomType;
import sootup.java.core.JavaIdentifierFactory;

/** @author Zun Wang */
public class AugEvalFunction {
IdentifierFactory factory = JavaIdentifierFactory.getInstance();

private final ImmutableSet<ClassType> evalClassTypes;
private final ClassType stringClassType;
private final ClassType classClassType;
private final ClassType methodHandleClassType;
private final ClassType methodTypeClassType;
private final ClassType throwableClassType;

View<? extends SootClass<?>> view;
PrimitiveHierarchy primitiveHierarchy = new PrimitiveHierarchy();

public AugEvalFunction(View<? extends SootClass<?>> view) {
this.view = view;

// one time setup
final IdentifierFactory identifierFactory = view.getIdentifierFactory();
evalClassTypes =
ImmutableSet.of(
identifierFactory.getClassType("java.lang.Object"),
identifierFactory.getClassType("java.lang.Cloneable"),
identifierFactory.getClassType("java.io.Serializable"));

stringClassType = identifierFactory.getClassType("java.lang.String");
swissiety marked this conversation as resolved.
Show resolved Hide resolved
classClassType = identifierFactory.getClassType("java.lang.Class");
methodHandleClassType = identifierFactory.getClassType("java.lang.MethodHandle");
methodTypeClassType = identifierFactory.getClassType("java.lang.MethodType");
throwableClassType = identifierFactory.getClassType("java.lang.Throwable");
}

/**
Expand All @@ -62,12 +81,15 @@ public Type evaluate(
@Nonnull Value value,
@Nonnull Stmt stmt,
@Nonnull StmtGraph<?> graph) {

// TODO: [ms] make use of the ValueVisitor..

if (value instanceof Immediate) {
if (value instanceof Local) {
return typing.getType((Local) value);
// if value instanceof Constant
} else {
if (value instanceof IntConstant) {
if (value.getClass() == IntConstant.class) {
int val = ((IntConstant) value).getValue();
if (val >= 0 && val < 2) {
return AugIntegerTypes.getInteger1();
Expand All @@ -84,22 +106,22 @@ public Type evaluate(
} else {
return PrimitiveType.getInt();
}
} else if (value instanceof LongConstant
|| value instanceof FloatConstant
|| value instanceof DoubleConstant
|| value instanceof NullConstant
|| value instanceof EnumConstant) {
} else if (value.getClass() == LongConstant.class
|| value.getClass() == FloatConstant.class
|| value.getClass() == DoubleConstant.class
|| value.getClass() == NullConstant.class
|| value.getClass() == EnumConstant.class) {
return value.getType();
} else if (value instanceof StringConstant) {
return factory.getClassType("java.lang.String");
} else if (value instanceof ClassConstant) {
return factory.getClassType("java.lang.Class");
} else if (value instanceof MethodHandle) {
return factory.getClassType("java.lang.MethodHandle");
} else if (value instanceof MethodType) {
return factory.getClassType("java.lang.MethodType");
} else if (value.getClass() == StringConstant.class) {
return stringClassType;
} else if (value.getClass() == ClassConstant.class) {
return classClassType;
} else if (value.getClass() == MethodHandle.class) {
return methodHandleClassType;
} else if (value.getClass() == MethodType.class) {
return methodTypeClassType;
} else {
throw new RuntimeException("Invaluable constant in AugEvalFunction: " + value);
throw new RuntimeException("Invaluable constant in AugEvalFunction '" + value + "'.");
}
}
} else if (value instanceof Expr) {
Expand All @@ -119,20 +141,23 @@ public Type evaluate(
return (tl instanceof PrimitiveType.IntType) ? PrimitiveType.getInt() : tl;
} else {
if (tl instanceof PrimitiveType.IntType && tr instanceof PrimitiveType.IntType) {
if (tl instanceof PrimitiveType.BooleanType) {
return (tr instanceof PrimitiveType.BooleanType) ? PrimitiveType.getBoolean() : tr;
} else if (tr instanceof PrimitiveType.BooleanType) {

if (tl.getClass() == PrimitiveType.BooleanType.class) {
return (tr.getClass() == PrimitiveType.BooleanType.class)
? PrimitiveType.getBoolean()
: tr;
} else if (tr.getClass() == PrimitiveType.BooleanType.class) {
return tl;
} else {
Collection<Type> set = primitiveHierarchy.getLeastCommonAncestor(tl, tr);
if (set.isEmpty()) {
Collection<Type> lca = PrimitiveHierarchy.getLeastCommonAncestor(tl, tr);
if (lca.isEmpty()) {
throw new RuntimeException(
"Invaluable expression by using AugEvalFunction: " + value);
"Invaluable expression by using AugEvalFunction '" + value + "'.");
}
return set.iterator().next();
return lca.iterator().next();
}
} else {
return (tl instanceof PrimitiveType.LongType) ? PrimitiveType.getLong() : tr;
return (tl.getClass() == PrimitiveType.LongType.class) ? PrimitiveType.getLong() : tr;
}
}
} else if (value instanceof AbstractFloatBinopExpr) {
Expand All @@ -151,27 +176,25 @@ public Type evaluate(
} else if (value instanceof Ref) {
if (value instanceof JCaughtExceptionRef) {
Set<ClassType> exceptionTypes = getExceptionTypeCandidates(stmt, graph);
ClassType throwable = factory.getClassType("java.lang.Throwable");
ClassType type = null;
for (ClassType exceptionType : exceptionTypes) {
Optional<?> exceptionClassOp = view.getClass(exceptionType);
SootClass<?> exceptionClass;
if (exceptionClassOp.isPresent()) {
exceptionClass = (SootClass<?>) exceptionClassOp.get();
} else {
throw new RuntimeException(
"ExceptionType: \"" + exceptionType + "\" is not in the view");
throw new RuntimeException("ExceptionType '" + exceptionType + "' is not in the view");
}
if (exceptionClass.isPhantomClass()) {
return throwable;
return throwableClassType;
} else if (type == null) {
type = exceptionType;
} else {
type = getLeastCommonExceptionType(type, exceptionType);
}
}
if (type == null) {
throw new RuntimeException("Invaluable reference in AugEvalFunction: " + value);
throw new RuntimeException("Invaluable reference in AugEvalFunction '" + value + "'.");
}
return type;
} else if (value instanceof JArrayRef) {
Expand All @@ -181,31 +204,16 @@ public Type evaluate(
// Because Object, Serializable and Cloneable are super types of any ArrayType, thus the
// base type of ArrayRef could be one of this three types
} else if (type instanceof ClassType) {
String name = ((ClassType) type).getFullyQualifiedName();
Type retType;
switch (name) {
case "java.lang.Object":
retType = factory.getClassType("java.lang.Object");
break;
case "java.lang.Cloneable":
retType = factory.getClassType("java.lang.Cloneable");
break;
case "java.io.Serializable":
retType = factory.getClassType("java.io.Serializable");
break;
default:
retType = BottomType.getInstance();
}
return retType;
return evalClassTypes.contains(type) ? type : BottomType.getInstance();
} else {
return BottomType.getInstance();
}
} else if (value instanceof JThisRef
|| value instanceof JParameterRef
} else if (value.getClass() == JThisRef.class
|| value.getClass() == JParameterRef.class
|| value instanceof JFieldRef) {
return value.getType();
} else {
throw new RuntimeException("Invaluable reference in AugEvalFunction: " + value);
throw new RuntimeException("Invaluable reference in AugEvalFunction '" + value + "'.");
}
}
return null;
Expand All @@ -225,20 +233,20 @@ private Set<ClassType> getExceptionTypeCandidates(
* type
*/
private Deque<ClassType> getExceptionPath(@Nonnull ClassType exceptionType) {
ViewTypeHierarchy hierarchy = new ViewTypeHierarchy(view);
ClassType throwable = factory.getClassType("java.lang.Throwable");
Deque<ClassType> path = new ArrayDeque<>();
path.push(exceptionType);

while (!exceptionType.equals(throwable)) {
ClassType superType = hierarchy.directSuperClassOf(exceptionType);
if (superType != null) {
path.push(superType);
exceptionType = superType;
} else {
throw new RuntimeException(
"The path from " + exceptionType + " to java.lang.Throwable cannot be found!");
while (exceptionType != throwableClassType) {
final Optional<? extends ClassType> superclassOpt =
view.getClass(exceptionType).flatMap(SootClass::getSuperclass);
if (!superclassOpt.isPresent()) {
throw new IllegalStateException(
"The path from '" + exceptionType + "' to java.lang.Throwable cannot be found!");
}

ClassType superType = superclassOpt.get();
path.push(superType);
exceptionType = superType;
}
return path;
}
Expand All @@ -250,13 +258,13 @@ private Deque<ClassType> getExceptionPath(@Nonnull ClassType exceptionType) {
* @param b an exception type
*/
private ClassType getLeastCommonExceptionType(@Nonnull ClassType a, @Nonnull ClassType b) {
if (a.equals(b)) {
if (a == b) {
return a;
}
ClassType commonType = null;
Deque<ClassType> pathA = getExceptionPath(a);
Deque<ClassType> pathB = getExceptionPath(b);
while (!pathA.isEmpty() && !pathB.isEmpty() && pathA.getFirst().equals(pathB.getFirst())) {
while (!pathA.isEmpty() && !pathB.isEmpty() && pathA.peekFirst().equals(pathB.peekFirst())) {
commonType = pathA.removeFirst();
pathB.removeFirst();
}
Expand Down
Loading