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

InvokeArgumentValidator changes #865

Draft
wants to merge 13 commits into
base: develop
Choose a base branch
from
74 changes: 74 additions & 0 deletions sootup.core/src/main/java/sootup/core/types/PrimitiveType.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,14 @@
*/

import javax.annotation.Nonnull;

import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import sootup.core.jimple.visitor.TypeVisitor;

import java.util.Map;
import java.util.Set;

/** Represents Java's primitive types. */
public abstract class PrimitiveType extends Type {

Expand All @@ -50,12 +56,32 @@ public String getName() {
return name;
}

@Nonnull
public abstract String getBoxedName();

@Override
@Nonnull
public String toString() {
return name;
}

private static final Map<Class<? extends PrimitiveType>, Set<Class<? extends PrimitiveType>>> implicitConversionMap =
ImmutableMap.<Class<? extends PrimitiveType>, Set<Class<? extends PrimitiveType>>>builder()
.put(ByteType.class, ImmutableSet.of(ShortType.class, IntType.class, LongType.class, FloatType.class, DoubleType.class))
.put(ShortType.class, ImmutableSet.of(IntType.class, LongType.class, FloatType.class, DoubleType.class))
.put(CharType.class, ImmutableSet.of(IntType.class, LongType.class, FloatType.class, DoubleType.class))
.put(IntType.class, ImmutableSet.of(LongType.class, FloatType.class, DoubleType.class))
.put(LongType.class, ImmutableSet.of(FloatType.class, DoubleType.class))
.put(FloatType.class, ImmutableSet.of(DoubleType.class))
.build();

public static boolean isImplicitlyConvertibleTo(PrimitiveType fromType, PrimitiveType toType) {
Class<? extends PrimitiveType> fromTypeClass = fromType.getClass();
Class<? extends PrimitiveType> toTypeClass = toType.getClass();
return implicitConversionMap.containsKey(fromTypeClass) && implicitConversionMap.get(fromTypeClass).contains(toTypeClass)
|| IntType.class.isAssignableFrom(fromTypeClass) && implicitConversionMap.get(IntType.class).contains(toTypeClass);
}

@Nonnull
public static ByteType getByte() {
return ByteType.getInstance();
Expand Down Expand Up @@ -107,6 +133,12 @@ public static ByteType getInstance() {
return INSTANCE;
}

@Nonnull
@Override
public String getBoxedName() {
return "Byte";
}

@Override
public void accept(@Nonnull TypeVisitor v) {
v.caseByteType();
Expand All @@ -124,6 +156,12 @@ public static ShortType getInstance() {
return INSTANCE;
}

@Nonnull
@Override
public String getBoxedName() {
return "Short";
}

@Override
public void accept(@Nonnull TypeVisitor v) {
v.caseShortType();
Expand All @@ -145,6 +183,12 @@ public static IntType getInstance() {
return INSTANCE;
}

@Nonnull
@Override
public String getBoxedName() {
return "Integer";
}

@Override
public void accept(@Nonnull TypeVisitor v) {
v.caseIntType();
Expand All @@ -162,6 +206,12 @@ public static DoubleType getInstance() {
return INSTANCE;
}

@Nonnull
@Override
public String getBoxedName() {
return "Double";
}

@Override
public void accept(@Nonnull TypeVisitor v) {
v.caseDoubleType();
Expand All @@ -179,6 +229,12 @@ public static LongType getInstance() {
return INSTANCE;
}

@Nonnull
@Override
public String getBoxedName() {
return "Long";
}

@Override
public void accept(@Nonnull TypeVisitor v) {
v.caseLongType();
Expand All @@ -196,6 +252,12 @@ public static FloatType getInstance() {
return INSTANCE;
}

@Nonnull
@Override
public String getBoxedName() {
return "Float";
}

@Override
public void accept(@Nonnull TypeVisitor v) {
v.caseFloatType();
Expand All @@ -213,6 +275,12 @@ public static CharType getInstance() {
return INSTANCE;
}

@Nonnull
@Override
public String getBoxedName() {
return "Character";
}

@Override
public void accept(@Nonnull TypeVisitor v) {
v.caseCharType();
Expand All @@ -230,6 +298,12 @@ public static BooleanType getInstance() {
return INSTANCE;
}

@Nonnull
@Override
public String getBoxedName() {
return "Boolean";
}

@Override
public void accept(@Nonnull TypeVisitor v) {
v.caseBooleanType();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,31 +22,80 @@
* #L%
*/

import java.util.List;
import sootup.core.IdentifierFactory;
import sootup.core.jimple.basic.Immediate;
import sootup.core.jimple.common.expr.AbstractInvokeExpr;
import sootup.core.jimple.common.stmt.Stmt;
import sootup.core.model.Body;
import sootup.core.signatures.MethodSignature;
import sootup.core.typehierarchy.TypeHierarchy;
import sootup.core.types.ClassType;
import sootup.core.types.PrimitiveType;
import sootup.core.types.Type;
import sootup.core.views.View;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
* A basic validator that checks whether the length of the invoke statement's argument list matches
* the length of the target methods's parameter type list.
*
* @author Steven Arzt
*/
public class InvokeArgumentValidator implements BodyValidator {
@Override
public List<ValidationException> validate(Body body, View view) {
List<ValidationException> validationException = new ArrayList<>();

for (Stmt stmt : body.getStmts()) {
if (stmt.containsInvokeExpr()) {
AbstractInvokeExpr invExpr =
stmt.getInvokeExpr();
MethodSignature callee = invExpr.getMethodSignature();
List<Immediate> args = invExpr.getArgs();
List<Type> parameterTypes = callee.getParameterTypes();
if (invExpr.getArgCount() != parameterTypes.size()) {
validationException.add(new ValidationException(stmt, "Invalid number of arguments"));
} else {
// check argument type
TypeHierarchy typeHierarchy = view.getTypeHierarchy();
IdentifierFactory identifierFactory = view.getIdentifierFactory();
Iterator<Immediate> iterArgs = args.iterator();
Iterator<Type> iterParameters = parameterTypes.iterator();
while (iterArgs.hasNext() && iterParameters.hasNext()) {
// handle implicit conversion cases. e.g., `int` is used as an argument of a `double` parameter
Type argType = iterArgs.next().getType();
Type paraType = iterParameters.next();
if (argType instanceof PrimitiveType && paraType instanceof PrimitiveType
&& PrimitiveType.isImplicitlyConvertibleTo((PrimitiveType) argType, (PrimitiveType) paraType)) {
continue;
}
// other cases
ClassType argClassType = getClassType(identifierFactory, argType);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After the primitive type check the argument could still have the type: arraytype. You can not assume it is automatically a classtype.

ClassType paramClassType = getClassType(identifierFactory, paraType);
if (argClassType != paramClassType && (!typeHierarchy.contains(paramClassType)
|| !typeHierarchy.subtypesOf(paramClassType).contains(argClassType))) {
validationException.add(new ValidationException(stmt,
String.format("Invalid argument type. Required %s but provided %s.", paraType, argType)));
}
}
}
}
Momo-Not-Emo marked this conversation as resolved.
Show resolved Hide resolved
}
return validationException;
}

@Override
public List<ValidationException> validate(Body body, View view) {
// TODO: check copied code from old soot
/*
* for (Unit u : body.getUnits()) { Stmt s = (Stmt) u; if (s.containsInvokeExpr()) { InvokeExpr iinvExpr =
* s.getInvokeExpr(); SootMethod callee = iinvExpr.getMethod(); if (callee != null && iinvExpr.getArgCount() !=
* callee.getParameterCount()) { exceptions.add(new ValidationException(s, "Invalid number of arguments")); } } }
*/
return null;
}
private ClassType getClassType(IdentifierFactory identifierFactory, Type type) {
if (type instanceof PrimitiveType)
return identifierFactory.getBoxedType((PrimitiveType) type);
else
return identifierFactory.getClassType(type.toString());
}

@Override
public boolean isBasicValidator() {
return true;
}
@Override
public boolean isBasicValidator() {
return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

import sootup.core.jimple.basic.Local;
import sootup.core.jimple.basic.Value;
import sootup.core.jimple.common.stmt.Stmt;
import sootup.core.model.SootClass;
import sootup.core.model.SootMethod;

Expand Down Expand Up @@ -58,4 +59,8 @@ public ValidationException(SootMethod method, String s, String s1) {
// TODO: auto generated stub

}

public ValidationException(Stmt stmt, String s) {
// TODO: auto generated stub
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,15 @@ public Optional<JavaSootClassSource> getClassSource(@Nonnull ClassType type, @No
try {
FileSystem fs = fileSystemCache.get(path);
final Path archiveRoot = fs.getPath("/");
JavaClassType javaClassType = null;

// This is a temporary workaround to prevent a casting exception when passing an anonymous ClassType object
if (type instanceof JavaClassType)
Momo-Not-Emo marked this conversation as resolved.
Show resolved Hide resolved
javaClassType = (JavaClassType) type;
else
javaClassType = new JavaClassType(type.getClassName(), type.getPackageName());
return getClassSourceInternal(
(JavaClassType) type, archiveRoot, new AsmJavaClassProvider(view));
javaClassType, archiveRoot, new AsmJavaClassProvider(view));
} catch (ExecutionException e) {
throw new RuntimeException("Failed to retrieve file system from cache for " + path, e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -236,10 +236,7 @@ public Collection<PrimitiveType> getAllPrimitiveTypes() {
@Override
@Nonnull
public JavaClassType getBoxedType(@Nonnull PrimitiveType primitiveType) {
String name = primitiveType.getName();
StringBuilder boxedname = new StringBuilder(name);
boxedname.setCharAt(0, Character.toUpperCase(boxedname.charAt(0)));
return getClassType(boxedname.toString(), "java.lang");
return getClassType(primitiveType.getBoxedName(), "java.lang");
}

@Override
Expand Down
Loading
Loading